JS作用域| 青训营笔记

101 阅读4分钟

变量的分类

全局变量、局部变量。

script标签内顶层定义的变量是全局变量,函数内(作用域内)定义的变量叫局部变量。

作用域

scope作用域是一个虚拟的概念。直观来说,作用域就是变量(常量)起作用的区域,数据可访问的区域。

JavaScript采用词法作用域(lexical scoping),也就是静态作用域,在程序写成的时刻,作用域就已经确定了

作用域分类

es5阶段使用var来定义变量,es6+使用let、const来定义。

es5的处理机制中,作用域分为全局作用域函数作用域。也就是说,var定义的变量只在函数内是局部变量,不在函数内定义就都是全局变量。

es6的处理机制中,作用域分为全局作用域块级作用域。函数也是属于块级作用域,简单来说,只要有花括号{}就会形成作用域

在es6中,条件语句、循环语句等等都会形成作用域。

     if (true) {
       let a = 123;
     }
     for(let b=0;b<10;b++){
 ​
     }
     {
       let c = 10;
     }

以上代码中的变量a、b、c都是局部变量。

作用域链

作用域可以形成树形结构,每个作用域都可以与上级作用域关联,直到全局作用域。由当前作用域逐级向全局作用域关联,形成的链条就叫做,作用域链。

我们访问变量时,先在当前作用域中查找,如果没有找到,则沿着作用域链向上级逐级查找,直到全局为止。

     var x = 1;
     fn();
     function fn() {
       alert(x);//undefined
       x++;
       alert(x);//NaN
       var x = 456;
       alert(x);//456
     }

当我们需要访问变量x时,首先在当前作用域内找局部变量x,如果找到了,则不会继续访问上层作用域。上例中,全局变量x不会影响到函数fn内,因为,fn内有局部变量x。

执行上下文

作用域是静态的,在代码写成时就已经确定了。

而执行上下文是代码在执行时才会创建的。

当程序被执行时:

1、首先生成全局执行上下文,并插入内存,且自动开始执行。

2、当执行到函数时,会再次生成函数的执行上下文(局部执行上下文),插入到调用函数的地方。

3、全局执行上下文全部执行完成,程序才算执行完成。

执行上下文的分类

执行上下文分为:全局执行上下文、局部执行上下文、eval方法形成的上下文(eval函数的作用是将字符串形式的js脚本执行)。

全局执行上下文在程序开始执行时就会生成,它是惟一的。

局部执行上下文,所有可以形成独立作用域的代码都会生成局部执行上下文。

eval方法形成的执行上下文也算作局部上下文,隶属于全局执行上下文的。

执行上下文的生命周期

执行上下文的生命周期包括三个阶段:创建阶段 → 执行阶段 → 回收阶段

创建阶段 :

  1. 确定变量:

    如果是函数则先确定参数且赋值。其次确定变量(函数),不赋值。

  2. 创建作用域链:

    根据作用域链,将链条上的执行上下文的内存地址确定并存储。

    为什么要存储执行上下文的内存地址,因为程序只有执行了(生成了执行上下文)才能确定变量的值。

  3. 确定this指向:

    this是一个虚拟的指针,存储的是执行上下文的内存地址。

执行阶段:

按顺序依次执行代码。(如果有函数或其他会生成独立作用域的语句执行,则会继续生成局部执行上下文)。

回收阶段:

等待js引擎的自动垃圾回收机制回收资源。

作用域与执行上下文的不同

作用域是静态的,程序写成的时候,作用域就可以确定了。

执行上下文是动态的,程序被调用时才会生成执行上下文。

例如:函数在写成时,作用域就确定了,被调用时才会生成执行上下文,如果被多次调用,会生成多个执行上下文。