JavaScript专家:为什么`with`会使编译器与范围相关的优化无效

时间:2017-09-11 17:37:48

标签: javascript v8 spidermonkey lexical-scope

阅读Kyle Simpson的“你不知道JS:范围&关闭,他认为你应该远离eval()函数和with关键字,因为只要编译器看到这两个(我是释义),它就不会执行一些与词法有关的优化-scope并存储标识符的位置,因为这些关键字可能会修改词法范围,从而使编译器的优化排序不正确(我假设优化类似于编译器存储每个标识符的位置,因此它可以提供标识符的值在运行时请求时不搜索它。

现在我理解为什么在使用eval()关键字时会发生这种情况:您的eval可能正在评估用户输入,而用户输入可能是新变量的声明,它会影响您正在访问的另一个变量稍后让我们说正在执行的函数,如果编译器存储了静态位置,则访问将返回错误标识符的值(因为访问应该返回eval()声明的标识符的值,但它返回了编译器存储的变量的值,以优化查找)。所以我只是假设这就是为什么编译器在代码中发现eval()时不会执行与范围相关的查找的原因。

但是为什么编译器对with关键字执行相同的操作?本书说它是这样做的,因为with在运行时创建了一个新的词法范围,它使用作为with参数传递的对象的属性来声明一些新的标识符。我真的不知道这意味着什么,我很难想象所有这些,因为本书中所有与编译器相关的东西都是理论。

我知道我可能走错了路,在这种情况下,请妥善纠正我的所有误解:)

2 个答案:

答案 0 :(得分:4)

这里提到的优化是基于这样的事实:函数中声明的变量总是可以通过对代码的简单静态分析来确定(即,通过查看var / let和{ {1}}声明),函数中声明的变量集永远不会改变。

function通过引入变异本地绑定的能力(通过在运行时向函数的范围引入新变量)来违反此假设。 eval通过在函数中引入新的非词法绑定来违反此假设,该函数的属性在运行时计算。静态代码分析无法始终确定with对象的属性,因此分析器无法确定with块中存在哪些变量。重要的是,提供给with的对象可能在函数的执行之间发生变化,这意味着函数的词汇部分中的变量集永远不能保证一致。

考虑简单的功能:

with

function foo() { var a, b; function c() { ... } ... } 中的所有点都有三个本地范围变量,fooab。优化器可以为函数附加一个永久性的“注释”,该函数说:“此函数有三个变量:cab。这永远不会改变。”< / p>

现在考虑:

c

function bar(egg) { var a, b; function c() { ... } with(egg) { ... } } 块中,不知道哪些变量将存在或不存在。如果with中有abc,我们直到运行时才会知道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 ??*/);
 }
}