让你轻松理解javascript作用域的相关概念

340 阅读4分钟

什么是作用域?

image.png

任何语言都有 作用域 的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域、局部作用域和块级作用域(es6)三种。

作用域就是一个独立的地盘,让变量不会外泄、暴露出去。主要作用就是隔离变量,使得不同作用域下的同名变量不会产生冲突。

全局作用域

首先让我们来认识一下什么是全局作用域。

全局作用域可以理解为是所有js代码中最外层那一层环境。因此根据我们对它的了解,我们自然可以知道,全局作用域中的变量可以在任意子作用域中访问、又因为它是所有作用域的祖先作用域。 最终得出结论:在js代码的任何地方都可以访问到全局作用域下的变量。 换个说法:可以在任何作用域下访问到的变量具有全局作用域

让我们看一段代码

<script>
  var a = 5;
  function func1() {
    var b = 10;
    console.log("b作用域下访问a:", a); //b作用域下访问a: 5
    console.log("b作用域下访问c:", c); //Uncaught ReferenceError: c is not defined
    function func2() {
      var c = 10;
      console.log("c作用域下访问a:", a); //c作用域下访问a: 5
    }
    func2();
  }
  func1();
</script>

通过这段代码我们可以得出结论: 变量a和函数func1具备全局作用域,变量b、c和函数func2不具备全局作用域。

  • window对象属于全局对象、他下面的属性也具备全局作用域。

通常我们在全局定义的属性和变量都会挂载到window对象下。这样当书写较多代码的时候会造成全局变量的污染。 es6出现let、const使得全局作用域下定义的变量不在挂在到window对象下了。

在一些框架和库中会使用函数的立即调用防止变量定义到全局下

(function(){})()

函数作用域

函数作用域、顾名思义函数内部的作用域,只在函数内部可以访问。

function func (){
    function func1(){
    
    }
}

从上段代码 我们可以分成两层、第一层是func内部、第二层使func1内部。我们知道在func1中可以访问func中的变量、但func不可以访问func1内部的变量。

很多初学者会认为是不是一个花括号{}就是一个作用域呢?

其实这是不对的,例如一些语句,例如 if(){} while() do{}while等{}并不能产生作用域。(这里暂时不考虑es6的块级作用域)

块级作用域

块级作用域是通过使用es6新增的let和const声明变量产生的,所声明的变量在指定块的作用域外无法被访问。 块级作用域是使用let、const在{}内部声明变量产生的。

让我们看一段代码 理解一下这段内容。

      {
        let a = 5;
      }
      console.log(a) //Uncaught ReferenceError: a is not defined

让我们复习一下let var const的区别

声明变量特点:

  • 不可以重复定义
  • 不挂在window对象下
  • 不存在变量提升
  • 存在块级作用域
  • 存在暂时性死区

nst声明变量除以上特点外还有 声明的是常量 果是基本数据类型则是不可变、如果是引用数据类型 则内容可变 但地址不可变。(可用Object.freeze(obj) 来冻结它,使它无法改变)

作用域链

首先让我们认识一下作用域链是什么?

⼀般情况下,变量取值到 创建 这个变量 的函数的作⽤域中取值。 但是如果在当前作⽤域中没有查到值,就会向上级作⽤域去查,直到查到全局作⽤ 域,这么⼀个查找过程形成的链条就叫做作⽤域链。

  var a = 1;
  function func1() {
    var b = 2;
    function func2() {
      console.log(a); // 1
      console.log(b); // 2
    }
    func2();
  }
  func1();

注意:作用域链取决于它创建时候的环境、并不取决于它执行的环境。 这句话十分重要一定要记住!!! 这一点与执行上下文不同。

让我们看一段代码理解理解

      var x = 1;
      function func() {
        console.log(x);
      }
      function func1() {
        function func2() {
          var x = 3;
          func();
        }
        func2();
      }
      func1();  //1
  • func()执行环境中声明了一个局部变量x=3。
  • func的定义环境下,声明了一个x=1
  • 最终结果输出1
  • 由此我们可以确定作用域链是指沿着定义时候的位置从内向外找。

作用域链的精髓在于上方的那句话一定要记住。!!!