作用域

117 阅读3分钟

1.作用域

作用域就是一套规则,用于确定在何处以及如何查找变量(标识符)的规则

我们在查找变量的时候,先在函数作用域中查找,没有找到,再去全局作用域中查找,有一个往外层查找的过程。我们好像是顺着一条链条从下往上查找变量,这条链条,我们就称之为作用域链。

作用域中变量(标识符)的查找规则

JavaScript是有编译过程的,var name = 'iceman'这段代码,其实这是有两个动作的:

  • 编译器在当前作用域中声明一个变量name
  • 运行时引擎在作用域中查找该变量,找到了name变量并为其赋值
console.log(name); // 输出undefined
var name = 'iceman'; 

在var name = 'iceman'的上一行输出name变量,并没有报错,输出undefined,说明输出的时候该变量已经存在了,只是没有赋值而已。

其实编译器是这样工作的,在代码执行之前从上到下的进行编译,当遇到某个用var声明的变量的时候,先检查在当前作用域下是否存在了该变量。如果存在,则忽略这个声明;如果不存在,则在当前作用域中声明该变量。 上面的这段简单的代码包含两种查找类型:输出变量的值的时候的查找类型是RHS,找到变量为其赋值的查找类型是LHS。

如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。

在作用域中查找变量都是RHS,并且查找的规则是从当前作用域开始找,如果没找到再到父级作用域中找,一层层往外找,如果在全局作用域如果还没找到的话,就会报错了:ReferenceError: 某变量 is not defined

function foo(a) { 
 var b = a; 
 return a + b; 
} 
var c = foo( 2 );
// 1. 找出所有的 LHS 查询(这里有 3 处!)c = ..;、a = 2(隐式变量分配)、b = ..
// 2. 找出所有的 RHS 查询(这里有 4 处!)foo(2..、= a;、a ..、.. b

词法作用域

JavaScript 采用的是词法作用域(静态作用域),函数的作用域在函数定义的时候(书写阶段)就决定了。而与词法作用域相对的是动态作用域,函数的作用域是在函数调用的时候才决定的。JavaScript采用的是静态作用域,this采用的是动态作用域

var value = 1;

function foo() {
    console.log(value);
}

function bar() {
    var value = 2;
    foo();
}

bar(); //打印1

每个函数内都有一个[[scope]] 这个是函数内部独有的,不会被外部访问到,他存储了执行上下文的集合,其实也就是作用域链,查找函数变量的时候,从函数的[[scope]]顶端开始查找

function a(){
    function b(){
        function c(){
        
        }
    }
    b()
}
a()

当a函数被定义时,a.[[scope]]  ---> 0:GO:{}
当a函数被执行时,a.[[scope]]  ---> 0:AO:{}
                                 1:Go:{}
                               
当b函数被定义时,他是捡a函数的[[scope]],b.[[scope]] ---> 0:aAO:{}
                                                      1:Go:{}
当b函数被执行时,在b.[[scope]].push AO,b.[[scope]] ---> 0:b->AO:{}
                                                      1:a->AO:{}
                                                      2:GO:{}
                                                      
当c函数被定义时,在c.[[scope]] ---> 0:bAO:{}
                                  1:aAO:{}
                                  2:GO:{}         
当c函数被执行时,在c.[[scope]] ---> 0:cAO:{}
                                  1:bAO:{}
                                  2:aAO:{}
                                  3:GO:{}