阅读Kyle Simpson的“你不知道JS:范围&关闭,他认为你应该远离eval()
函数和with
关键字,因为只要编译器看到这两个(我是释义),它就不会执行一些与词法有关的优化-scope并存储标识符的位置,因为这些关键字可能会修改词法范围,从而使编译器的优化排序不正确(我假设优化类似于编译器存储每个标识符的位置,因此它可以提供标识符的值在运行时请求时不搜索它。
现在我理解为什么在使用eval()
关键字时会发生这种情况:您的eval可能正在评估用户输入,而用户输入可能是新变量的声明,它会影响您正在访问的另一个变量稍后让我们说正在执行的函数,如果编译器存储了静态位置,则访问将返回错误标识符的值(因为访问应该返回eval()
声明的标识符的值,但它返回了编译器存储的变量的值,以优化查找)。所以我只是假设这就是为什么编译器在代码中发现eval()
时不会执行与范围相关的查找的原因。
但是为什么编译器对with
关键字执行相同的操作?本书说它是这样做的,因为with
在运行时创建了一个新的词法范围,它使用作为with
参数传递的对象的属性来声明一些新的标识符。我真的不知道这意味着什么,我很难想象所有这些,因为本书中所有与编译器相关的东西都是理论。
我知道我可能走错了路,在这种情况下,请妥善纠正我的所有误解:)
答案 0 :(得分:4)
这里提到的优化是基于这样的事实:函数中声明的变量总是可以通过对代码的简单静态分析来确定(即,通过查看var
/ let
和{ {1}}声明),函数中声明的变量集永远不会改变。
function
通过引入变异本地绑定的能力(通过在运行时向函数的范围引入新变量)来违反此假设。 eval
通过在函数中引入新的非词法绑定来违反此假设,该函数的属性在运行时计算。静态代码分析无法始终确定with
对象的属性,因此分析器无法确定with
块中存在哪些变量。重要的是,提供给with
的对象可能在函数的执行之间发生变化,这意味着函数的词汇部分中的变量集永远不能保证一致。
考虑简单的功能:
with
function foo() {
var a, b;
function c() { ... }
...
}
中的所有点都有三个本地范围变量,foo
,a
和b
。优化器可以为函数附加一个永久性的“注释”,该函数说:“此函数有三个变量:c
,a
和b
。这永远不会改变。”< / p>
现在考虑:
c
在function bar(egg) {
var a, b;
function c() { ... }
with(egg) {
...
}
}
块中,不知道哪些变量将存在或不存在。如果with
中有a
,b
或c
,我们直到运行时才会知道with
或一个由bar
词法范围创建的。
为了展示这是一个问题的半实际例子,最后考虑:
with(egg)
当内部函数执行时(例如,function baz(egg) {
with(egg) {
return function() { return whereami; }
}
}
),执行引擎将查找范围链以查找bar({...})()
。如果允许优化器将永久范围注释附加到whereami
,则执行引擎会立即知道在函数的baz
闭包中查找baz
的值,因为那样保证成为whereami
的主页(范围链中任何类似命名的变量都会被最近的变量遮蔽)。但是, 不知道whereami
中是否存在whereami
,因为它可以通过特定运行中baz
的内容有条件地创建创建该内部函数的egg
。因此,必须检查,并且不使用优化。
答案 1 :(得分:0)
举个例子:
{
let a = 1; //stored at 123
{
let b = 2; //stored at 124
console.log(a/*123*/,b/*124*/);
}
}
现在这个:
{
let a = 1;//stored at 123
with({a:3}){
console.log(a /*123 ??*/);
}
}