1

1.词法作用域

词法作用域又被称为静态作用域。函数的词法作用域是由函数的声明位置决定的。函数一旦声明,他的词法作用域就确定了。一个函数的词法作用域包括这个函数的形参、函数内部声明的变量以及函数内部声明的函数。我们可以将“词法作用域”中的“词法”理解为声明的标识符。

//在全局下 找到了词 foo
function foo(a) {
    //foo下 找到了词 a , b , bar
    var b = a * 2;
    function bar(c) {
        //在bar下 找到了词 c
        console.log(a, b, c);
    }
    bar(b * 3);
}
foo();

相应地,也有动态作用域的概念,指的是函数的作用域由函数的调用位置决定。动态作用域在JS中不存在。

2.作用域

  1. 作用域是一套规则,用来确定在何处以及如何查找标识符。
  2. 作用域变量的查找机制:在当前作用域下无法查找到某个变量时 ,引擎就会在外层嵌套的作用域下继续查找, 直到找到该变量 或者抵达最外层的全局作用域为止。
  3. 作用域链用来进行标识符的查询,保证对变量函数的有序访问的。
 function foo(a) {
     var b = a * 2;
     function bar(c) {
         console.log(a, b, c);
     }
     bar(b * 3);
 }
 foo();

在上面这段代码中,bar()在执行时,会去查找abc这三个变量,由于自身的作用域内,只存在了形参c,没有声明ab变量,根据作用域变量的查找机制,会去外层的foo()中查找ab,在foo()中查找到了形参a与声明的变量b,与是停止查找开始赋值执行代码。在查找时,是根据作用域链来决定查找顺序的。
bar()的作用域链: bar=>foo=>全局作用域(window)。foo()的作用域链:foo()=>全局作用域(window)。

3.函数的执行环境

执行环境也叫执行上下文,每一个执行环境都有一个与之关联的变量对象,在这个环境中定义的所有的函数和变量都会保存在这个对象上,包括argumentsthis
函数的执行环境实在函数执行时确定的,函数执行时,所需的变量都是从函数的执行环境中取出的,因此执行环境中变量的值,是会随着函数执行而发生变化的。
函数的执行环境在函数执行时会经历三个阶段,创建阶段、执行阶段、销毁。所以每次执行函数,执行环境都是一个新的执行环境。下面的函数在执行时:

 var a = 2;
 function fn(x) {
     //arguments
     //this
     var b = 2;
     return a + b + x;
 }
 fn(0);

首先会创建执行环境,在创建阶段有三个主要活动:
1).生成变量对象
2).建立作用域链
3).确定 this 指向

此时的变量对象:

 fn(0)的执行环境的变量对象:
         {
           x:0,
           a:2,//来自全局执行环境
           b:undefined,//此时还未执行 尚未赋值
           arguments:[],//还未执行 尚未传入参数
           this:window
         }

创建完成之后,进入执行阶段,执行阶段的执行环境变量对象被称为活动对象。在执行阶段,主要活动有:
1).变量赋值
2).函数引用
3).执行其他代码
此时,执行环境活动对象为:

 fn(0)的执行环境的变量对象:
{
    x: 0,
    a: 2, //来自全局执行环境
    b: 2, //赋值
    arguments: [0], //传入一个参数 0
    this: window,
};

在函数执行完成之后,执行环境会从执行环境栈中被移出销毁。下次执行环境时,会重新创建一个执行环境。

4.执行环境栈

执行环境栈其实就是一个执行环境压栈和出栈的过程。
在浏览器第一次加载JS时,就默认进入了全局执行环境,此时便会将全局执行环境压入栈中。当退出浏览器时,全局执行环境也会被销毁。

var a = 1;
 var b = 2;
 function fn(x) {
     //arguments
     //this
     var a = 10;
     function bar(x) {
         var a = 100;
         b = x + a;
     }
     bar(20);
     bar(200);
     return b;
 }
 fn(0);

上面这段代码,
加载完毕之后,全局环境被压入执行环境栈,此时全局执行环境为活跃状态,
在执行fn(0)时,会将fn(0)的执行环境压入执行环境栈,此时栈中有全局执行环境和fn(0)的执行环境并且fn(0)的执行环境进入了活跃状态,
fn(0)的执行至bar(20)时,会将bar(20)的执行环境压入执行环境栈,此时bar(20)的执行环境成为了活跃状态,
bar(20)执行完成,会将bar(20)的执行环境从栈中取出销毁,此时活跃状态回归到了fn(0)的执行环境
随后执行bar(200);重复了一遍执行bar(20)的过程。
随后fn(0)执行完毕,fn(0)的执行环境被从栈中取出销毁,活跃状态回归到了全局执行环境。

函数作用域与执行环境的不同

  1. 作用域在函数声明时就已经确定了,所以作用域是静态的。
  2. 每个函数都会创建自己的作用域,通过作用域可以查找到当前作用域范围内的变量和函数有哪些,但是不知道变量的值。
  3. 执行环境是在函数执行时确定的,包含了作用域内的所有变量和函数的值,执行环境是动态的。
  4. 每次函数执行时的执行环境都是不同的,每次执行都是创建一个新的执行环境,形成新的执行环境栈。
注:以上仅为个人学习理解,如有错误,谢谢指正。

forceddd
271 声望912 粉丝

一名前端爱好者。