词法作用域
词法作用域-
作用域查找会在找到第一个匹配的标识符时停止,内部标识符会屏蔽同名外部标识符。
-
全局变量会自动成为全局对象(比如window或global),但这里有点小小的区别:
var a=1; b=2; function(){ var c=3; d=4; }
若上面代码在浏览器中,全局对象是
window
,a,b,d都会成为window
的属性,即均可以通过window.a
访问(非严格模式下,严格模式下不通过var声明会报错)。
若上面的代码在vscode中,全局对象是global
,只有b,d会成为global
的属性(非严格模式下,严格模式下不通过var声明会报错),global
不会自动把用var声明的变量添加为自己的属性。
欺骗词法
词法作用域完全由写代码期间函数所声明的位置来定义,通过欺骗词法可以在运行时修改词法作用域,但是会导致性能下降。
-
eval(...)
函数(在严格模式或ES6中被限制,不建议使用)
接受一个字符串参数,可以在你写的代码中用程序生成代码并运行,就好像代码是写在那个位置一样。如:function foo(str,a){ eval(str); console.log(a,b); } var b=2; foo("var b=3;",1);//1 3
eval(...)
通常用来执行动态创建的代码。 -
with
关键字(在严格模式或ES6中被禁止使用)
with通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。function foo(obj){ with(obj){ a=3; } } var o1={a:2}; var o2={b:2}; foo(o1); foo(o2); console.log(o1.a);//3 console.log(o2.a);//undefined console.log(a);//3
在上面的代码中,
with
会在代码执行时动态地延长作用域链(另外一个延长作用域链的方法是try-catch
语句块中的catch
块),因为o2没有a属性,所以通过LHS查询,引擎在全局环境创建了一个变量a并将3赋值给a,所以o2.a为undefined。这里有个要注意的问题就是,访问变量和访问对象的属性是不一样的:
- 访问变量时会LHS查询和RHS查询,在嵌套的作用域链中查询。
赋值或者取值分别按照第一章所说的规则进行LHS查询和RHS查询 - 访问对象的属性则是通过对象的
[[Put]]
操作和[[Get]]
操作来查找,在整个原型链([[prototype]]
链)查找
赋值时,通过[[Put]]
操作,若变量存在则将新的值赋值给变量(实际上情况很复杂,详见第二部分第2章和第5章,p.117),若不存在,则为该对象创建该属性变量并赋值;取值时,通过[[Get]]
操作取,若没有找到则遍历原型链,还是没有找到则返回undefined。
所以上面代码改成下面的就会输出不同结果:
function foo(obj){ obj.a=3; } var o1={a:2}; var o2={b:2}; foo(o1); foo(o2); console.log(o1.a);//3 console.log(o2.a);//3 console.log(a);//ReferenceError: a is not defined(即a未声明undeclared)
- 访问变量时会LHS查询和RHS查询,在嵌套的作用域链中查询。
网友评论