JavaScript中作用域及作用域链详解

作用域(Scope)

作用域概述

​ 任何程序设计语言都有作用域的概念,简单地说,作用域就是变量与函数可访问的范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域分为全局作用域(Global Scope)和局部作用域(Local Scope)(ps:在ES5中没有块级作用域)相应的,变量在全局作用域下称为全局变量,在局部作用域下称为局部变量。

全局作用域

​ 在整个程序生命周期内都是有效的,在任意的函数内部都能访问的变量或函数拥有全局作用域。通俗的说就是全局声明的变量和定义的函数,在JS代码任何位置都可以使用。以下几种情形拥有全局作用域

在最外层定义的变量和函数拥有全局作用域

<script>
   var num = 10;

   function fn() {
      var sum = 0;

      function getSum() {
         console.log(sum);
      }
         getSum();
   }
   console.log(num); //打印10
   console.log(sum); //报错,变量sum不是全局变量
   fn(); //打印0 
   getSum(); //报错 getSum函数在fn函数内部

</script>

没有声明直接赋值的变量拥有全局作用域

<script>
    function fn() {
        num = 10; //没有声明,直接赋值
    }
    fn();
    console.log(num); //打印10
</script>

定义在window对象上的属性和方法,拥有全局作用域

<script>
   window.num = 10;

   function fn() {
      console.log(num); //打印10
      console.log(window.parseInt("10A")); //打印10
   }
   fn();
</script>

局部作用域

​ 与全局作用域相反,局部作用域一般只在固定代码片段可访问到。最常见的例如函数内部声明的变量。在某个函数内部声明的变量或函数拥有局部作用域。它们只能被该函数的语句使用,函数外部是不可访问的。函数的参数也属于函数内部的变量,因此拥有局部作用域。

<script>
   function fn(n) { //n为形参
      var num = 10;
      var sum = 0;
      sum = num + n;
      console.log(sum); //打印30
   };
   fn(20); //传入参数
   console.log(num); //报错 num不是全局变量
</script>

ES6的块级作用域

  • 在某个块("{ }")的内部声明的变量拥有块级作用域(在块级作用域内部定义函数需要特殊对待)。它们只能被该代码块内部的语句使用,代码块外部不可访问。代码块在创建的时候,变量的块级作用域已经确定下来。块级作用域和函数作用域也可以统称为局部作用域。
  • ES5 只有全局作用域和函数作用域,没有块级作用域。ES6的 let 声明的变量只在它所在的代码块内有效,实际上就是为JavaScript新增了块级作用域。
  • ES5 规定,函数只能在全局作用域和函数作用域之中声明。ES6 引入了块级作用域,明确允许在块级作用域之中声明函数。ES6 规定,在块级作用域之中,函数声明语句的行为类似于let,在块级作用域之外不可引用。但是这样的处理规则显然会对老代码产生很大的影响,出于兼容老代码的考虑,在块级作用域中声明的函数依然可以在作用域外部引用。如果需要函数只在块级作用域中起作用,可以写成函数表达式,而不是函数声明语句。
<script>
   if (1 >= 0) {
       let num = 1;
       let fn = function() {
           console.log(num + 1);
       }
   }
   console.log(num); //报错
   fn(); //报错
</script>

作用域链

​ 当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法搜索到某个变量时,引擎就会在外层嵌套的作用域中继续搜索,直到搜索到该变量,或抵达最外层的作用域(也就是全局作用域)为止。这样一条有序的列表,称为作用域链,作用域链的最前端一定是当前作用域。通俗的讲:先在当前自己的作用域的中找有没有声明的变量,如果没有往上层作用域找,如果上层作用域也没有该变量,就报错。

<script>
   var num = 10086;

   function getNum() {
   var number = 10010;

      function swapNum() {
         var tempNumber = number;
         number = num;
         num = tempNumber;
         //在这里可以访问到num,number,tempNumber;
          console.log(num, number, tempNumber);
      }
      swapNum();
      //在这里可以访问到num,number;
      console.log(num, number);
   }
   //在这里只访问到num;
   console.log(num);
   getNum();
</script>

​ 上面共涉及到三个执行环境:全局作用域环境、getNum()局部作用域环境以及swapNum()局部环境。
在全局变量环境中有一个变量num和一个函数getNum()。getNum()局部作用域环境中有一个变量number和一个函数swapNum()。但它也可以访问到全局作用域环境中的num变量。swapNum()局部作用域环境中有一个变量tempNumber,该变量只可以在这个环境中访问到。然而在swapNum()内部则可以访问其他两个环境中的所有变量,因为那两个环境是它的父级作用域环境。

​ 总而言之,内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。

​ 在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取和存储数据。该过程从作用域链头部,也就是从活动对象开始搜索,查找同名的标识符,如果找到了就使用这个标识符对应的变量,如果没找到继续搜索作用域链中的下一个对象,如果搜索完所有对象都未找到,则认为该标识符未定义。函数执行过程中,每个标识符都要经历这样的搜索过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值