一.欺骗词法作用域
1.eval
①eval
函数可以接收一个字符串作为参数,以动态形式插入程序的某个位置,并对其词法作用域的环境进行修改。(可在运行期修改书写期的词法作用域)
function foo(str,a){
eval(str);
console.log(a,b);
}
var b = 2;
foo('var b = 5;',1);
>>>1 5
②词法作用域是什么?
简单来说,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的。因此,当词法分析器处理代码时会保持作用域不变(大部分情况下这样)。
③在严格模式下, eval()
函数在运行时拥有自己的词法作用域。这意味着其中的声明无法修改所在的作用域。
function foo(str,a){
"use strict"
eval(str);
console.log(a,b);
}
var b = 2;
foo('var b = 5;',1);
>>>1 2
function foo(str,a){
"use strict"
eval(str);
console.log(a,b);
}
foo('var b = 5;',1);
>>>Uncaught ReferenceError: b is not defined
2.with
①with
通常被当做重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身:
var obj = {
a: 1,
b: 2,
c: 3
};
obj.a = 2;
obj.b = 3;
obj.c = 4;
with(obj) {
a = 3;
b = 4;
c = 5;
}
②实际上不仅仅是为了方便地访问对象属性:
function foo(obj){
with(obj){
a = 2;
}
}
var o1 = {
a: 3
};
var o2 = {
b: 3
};
foo(o1);
o1.a;
>>>2
foo(o2);
o2.a;
>>>undefined
a;
>>>2
//a被泄露到全局作用域中。因为,在非严格模式下,若a=2中的变量a未声明,
也就是在任何作用域中都查找不到变量a,那么就会在全局作用域中创建一个变量a,并将2赋值于它。
fooo1().png
foo(o2).png
③尽管with
块可以将一个对象处理为词法作用域,但是这个块内部正常的变量声明并不会被限制在这个块的作用域中,而是被添加到with
所处的函数的作用域中。
④with声明实际上是根据你传递给它的对象凭空创建了一个全新的词法作用域。
⑤当我们传递对象o1
给with
时,with
所声明的作用域是o1
,而这个作用域中含有一个与o1.a
属性同名的标识符。但当我们将o2
对象作为作用域时,其中没有a
标识符,因此进行了正常的LHS (Left Hand Side)
查找,[最后导致创建了一个全局变量a并赋值为2]。
⑥本质上:通过将一个对象的引用当成作用域来处理,将对象的属性当成作用域中的标识符来处理,从而创建了一个新的词法作用域。(同样也在运行时)
3.性能
①JavaScript引擎会在编译阶段进行数项的性能优化。其中有些优化依赖与能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。但是eval()
和with
的存在影响了引擎的性能。
②eval()
和with
还有个问题是:会被严格模式限制。所以,避免使用它们。
网友评论