第一部分 第2章 词法作用域

词法作用域

词法作用域
  • 作用域查找会在找到第一个匹配的标识符时停止,内部标识符会屏蔽同名外部标识符。

  • 全局变量会自动成为全局对象(比如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。

    这里有个要注意的问题就是,访问变量和访问对象的属性是不一样的:

    1. 访问变量时会LHS查询和RHS查询,在嵌套的作用域链中查询。
      赋值或者取值分别按照第一章所说的规则进行LHS查询和RHS查询
    2. 访问对象的属性则是通过对象的[[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)
    

你可能感兴趣的:(第一部分 第2章 词法作用域)