谈谈作用域和作用域链

1,033 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

前言

刚刚跟同事聊天中,无意中问到闭包的问题。在畅谈了一段时间之后,发现要掌握闭包知识,前置条件也不能少啊。我们的作用域和作用域链还是要好好谈谈的。

作用域 & 作用域链

作用域(scope)

  • 浅显的理解: 作用域就是变量的可用范围(scope)
  • 为什么要有作用域: 目的是防止不同范围的变量之间互相干扰。 例:
     var x=1;
     
     function f(){
         var y=2;
         
         function z(){
             var z=3;
         }
     }

js中包括2级作用域

  1. 全局作用域
  • 不属于任何函数的外部范围称为全局作用域。
  • 保存在全局作用域的变量称为全局变量-----如上诉代码中的 x

全局变量的特点

- 优点: 可反复使用 
- 缺点: 全局污染——开发时禁止使用

2. 函数作用域

  • 一个函数内的范围称为函数作用域
  • 保存在函数作用域内的变量称为局部变量

特例: 形参变量也是函数内的局部变量

  • 形参变量虽然没有用var声明
  • 但是形参变量也是函数内的局部变量! 例:
     var x=1;
     
     function f(y){ //y--函数内的局部变量
     
         function z(){
             var z=3;
         }
     }

局部变量的特点

  • 优点: 不会被污染
  • 缺点: 无法反复使用

非常重要: 只有函数的{},才能形成作用域

  • 不是所有{}都能形成作用域。
  • 也不是所有{}内的数据都能是局部变量
  • 比如:
    • 对象的{},就不是作用域!
    • 对象中的属性,也不是局部变量

image.png

  • 除函数{}之外的其余{},都不是作用域。
  • 都拦不住内部的变量超出{}的范围影响外部程序 例:
    console.log(a);//undefined
    if(false){
        var a=10;
    }
    console.log(a);//undefined

作用域链(scopes / scope chain)

其实每个函数在定义时,就已经规划好了自己专属的一个查找变量的路线图,称为作用域链 比如:

     var x=1;
     
     function f(){
         var y=2;
         
         function z(){
             var z=3;
         }
     }

当我们定义函数z()时,函数z就为自己规划好了一个由内向外的查找路线。 以防未来运行时,一旦自己缺少变量,应该去找谁——未雨绸缪。

  • 一个函数可用的所有作用域串联起来,就行成了当前函数的作用域链。

特殊: 给从未声明过的变量赋值(不报错,而是自动在全局创建变量)

总结

Js中只有两种局部变量:

  1. 函数内var出来的
  2. 函数的形参变量

看不见var,形参里也没有,就不是局部变量。

强调

形参变量也是函数的局部变量,函数传参采用的是按值传递。原始类型的值,在传参时,是将原变量的值复制一个副本给函数形参变量。所以,在函数内,修改形参变量,不影响外部原变量的值。 看代码理解一下:

    var a=10;
    function fun(a){
        a++;
        console.log(a);
    }
    fun(a); // ?
    console.log(a);

作用域的本质

JS中,作用域和作用域链都是对象结构

总结: 全局作用域其实是一个名为window的对象,所有全局变量和全局函数都是window对象的成员。

总结: 函数作用域其实是js引擎在调用函数时才临时创建的一个作用域对象。其中保存函数的局部变量。而函数调用完,函数作用域对象就释放了。

所以:JS中函数作用域对象,还有个别名——”活动的对象(Actived Object)”简称, AO。所以,局部变量不可重用。

举个例子:

    var a=10;
    //new Function()
        function fun(){ 
            var a=100;  //临时创建 函数作用域对象地址
            a++;        //按照顺序进行执行代码
            console.log(a);  
        }
    fun();          //创建地址----new Function()
    //释放函数、作用域对象,局部变量紧跟着释放,所以局部变量在函数调用后就不存在了
    console.log(a);