• JS作用域,作用域,作用链详解


    前言

      通过本文,你大概明白作用域,作用域链是什么,毕竟这也算JS中的基本概念。

    一.作用域(scope)

      什么是作用域,你可以理解为你所声明变量的可用范围,我在某个范围内申明了一个变量,且这个变量能在这个范围内可用, 那么我可以说此范围就是该变量的作用域。

      作用域一般分为局部作用域全局作用域。

      怎么理解呢,先说局部作用域。假设我在A范围内用var申明了一个变量a,变量a只能在范围A内使用,超出A范围就调用不到,那么这样的变量a就是局部变量,范围A就是所说的局部作用域。

    1 function A(){
    2     var a = 1;
    3     console.log(a);
    4 }
    5 A();//1
    6 console.log(a);//报错,A未定义

      假设我们在全局范围(没在任何函数体内)申明了变量b,你可以在任何局部作用域内去使用它,那么我们一般称b为全局变量,包含b的范围就是全局作用域,b随处可用,没任何限制。

    1 var b = 1;
    2 function B(){
    3     console.log(b);
    4 }
    5 B();//1
    6 console.log(b);//1

      我们在上面说,在A范围内用var申明变量a,a为局部变量,当我们不用var申明时,即使在一个局部作用域内,它依旧属于全局变量。

    1 function A(){
    2     a = 1;
    3     console.log(a);
    4 }
    5 A();//1
    6 console.log(a);//1

      只要一个变量申明前面未加任何申明符,那此变量就是全局变量,我们很少这样去做,因为很难保证后期维护不会导致变量申明重名,这样的做法容易造成全局污染。

      顺带一提,在函数体内,局部变量的优先级高于同名的全局变量(函数形参也是局部变量),如下:

    1 var a = 1;
    2 function A(a){
    3     console.log(a);
    4 }
    5 A(10);//10

      当然,函数的局部变量会被已存在的局部变量所覆盖:

    1 function A(a){
    2     var a = 1;
    3     console.log(a);
    4 }
    5 A(10);//1

      有一点需要注意的是,我们在实际开发中往往会遇到作用域嵌套,其实只要清楚作用域间变量是否能使用,是否会被覆盖的关系,就会很清晰了。

     1 var a = 1;
     2 function A(){
     3     var a = 2;
     4     function B(){
     5         var a = 3;
     6         console.log(a);
     7     }
     8     return B();
     9 }
    10 A();//3

    二.变量提升

      准确来说,在ES6中新增的申明符let已经解决了变量提升这种不严谨的问题,我在这里简单提提,之前也有一篇博文是专门介绍变量提升的。

      什么是变量提升呢,就是说,一个变量在对应的作用域内是随处可见的,意思是,就算使用在前,申明在后,它依旧能够使用,不会报错。

      具体想了解看这篇文章吧--申明提前,变量申明提前,函数申明提前,申明提前的先后顺序

    三.作用域链

      我们在上面说,作用域也存在嵌套的问题,那么我们可以这样去理解作用域链,当我们需要某个变量的值时,我们先去理它最近的作用域去找,如果找不套,就找它的上级作用域,依次类推,直到找到全局,如果全都未定义,那就抛出一个错误,如下。

    1 var a = 1
    2 function A(){
    3     function B(){
    4         console.log(a);
    5     }
    6     return B();
    7 }
    8 A();//1

      可以说,作用链的寻找过程是从内向外的过程,而不是从外到内,可以站在局部作用域去调用全局作用域的属性,反过来是不允许的。

     OK,概念大概说到这里吧,嗯,还是来几道题目巩固下。

    题目一

    1 var a=10; 
    2 function A(){ 
    3  alert(a);
    4 };            
    5 function B(){
    6  var a=20;
    7  A();
    8 }
    9 B();//10

      为什么输出10,而不是20?js中变量的作用域链与定义时的环境有关,与执行时无关。

      我们调用函数B后,函数B又调用了函数A,函数A里面没定义变量a但是要用a,函数A只是被B调用且不传参,因此函数A无权使用函数B的局部变量a,而我们在上方还有一个全局变量a,因此这里输出10.

    1 var a=10; 
    2 function A(a){ 
    3   alert(a);
    4 };            
    5 function B(){
    6   var a=20;
    7   A(a);
    8 }
    9 B();//20

      这样就可以输出20了,函数B调用函数A的同时传入参数a,函数B提供了变量a,那就输出20了。

    题目二

    1 function aaa(){
    2    a=10; 
    3 }
    4 alert(a);//报错

      想到了一个问题,这里就将题目稍作修改了。我们在前面说,在局部作用域内,不用var 申明一个变量,它也是全局变量,随处可见,那为何在外输出它会报错呢?

      函数是个很奇怪的东西,可以这样理解,一个函数当没被调用,它其实是不可见的,它里面的所有变量也不可见,即使a确实是全局变量没错,它还是在是否可见上受到了函数的限制,调用后就可见了。

    1 function A(){
    2    a=10; 
    3 }
    4 A()
    5 alert(a);//10

      那我们改成这样呢?

    1 alert(a);//报错
    2 function A(){
    3    a=10; 
    4 }
    5 A();
    6 alert(a)

      不是说函数调用后里面的变量a就随便用了吗,怎么上面的又报错了?因为代码都有自己的执行顺序,要知道第一次alert a,此时函数还未被调用,所以就报错了,有点绕,试着理解。

      就整理这么多吧,希望有所帮助。

    本文思路参考了 博文

    JS作用域面试题总结

    js作用域与原型的笔试题

  • 相关阅读:
    ubuntu查看安装的pytorch/cuda版本
    go不使用工具包将大写字符转成小写字符的方法
    使用Nexus搭建Maven私服
    maven setting.xml配置说明
    maven的仓库、生命周期与插件
    maven项目搭建
    maven之详解继承与聚合
    Maven核心概念之依赖,聚合与继承
    commons-logging日志系统
    新建我的 第一个maven项目
  • 原文地址:https://www.cnblogs.com/echolun/p/8796461.html
Copyright © 2020-2023  润新知