浅谈js作用域,作用域链

184 阅读6分钟

前言

         本文只是学习期间对js作用域和作用域链,自己的一点小感悟,以便以后自己复习所用,如果三生有幸能给您能带来一些没齿难忘的小收获的话,那也属于是感动天地了!本文是站在大佬的肩膀上加上了一些自己浅薄的理解原文:www.cnblogs.com/echolun/p/8…

一.作用域(scope)

         作用域是什么?可以理解为我们肆意妄为声明的变量可以使用的范围。我在某个范围内声明使用了一个变量,且能够在这个范围内使用它,那么这个范围就是我之前声明变量的作用域。

        在es5中只有全局和局部两个作用域 。怎么理解呢,先说局部作用域,比如说我在函数fn里面声明定义了一个变量a,var a = 1; 变量a就只能在函数fn里面使用,如果是超出了函数fn的范围就访问不到,那么函数fn即使局部作用域

function fn (){
    var a =  10;
    console.log(a)
}
fn() //10
console.log(a) //此时就会报错

         假设我们在全局范围内声明了一个变量b,var b = 2; 此时你可以在任何局部作用域中去使用它,那么我们称b为全局变量,而声明b的范围就是全局作用域,b随处可用,没有限制。

var b = 2;
function fn (){
    console.log(b)
}

fn() //2
console.log(b) //2

       怎么理解呢,全局中定义的变量b,就是可以想象成我们在学校里面的全校通报,比如老王昨天晚上犯了个错误被老师当场抓住了,到了第二天在学校门口的黑板上面就贴着一个通报批评“老王昨天晚上怎么怎么样啦,特此通报,以儆效尤之类的”这就相当于我们全局定义的全局变量b,每个班级就相当于那个函数fn,函数fn里面的学生都看到了学校门口的通报,他们都开始议论起来见面就拿老王昨天犯的错误起哄;而局部变量a,我们可以想象成是老王昨天晚上在寝室里面犯的错误,毕竟是错误,都想着隐瞒,老王说了,今天晚上的这件事情,只有寝室里面我们俩知道,绝不能让第三个人知道。毕竟不是什么见得了光的事情,绝不能让他泄漏出去让所有人都知道了,挺难为情的嘛。恰恰这个人口风很紧一个字都没往外说,所以全局就访问不到变量a啦。

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

function fn (){
    c = 3;
    console.log(c)
}
fn() //3
console.log(c) //3

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

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

var d = 4;
function fn(d){
    console.log(d)
}
fn(5); //5

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

function fn(e){
    var e = 6;
    console.log(e)
}
fn(7); //6

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

var a = 1;
function A(){
    var a = 2;
    function B(){
        var a = 3;
        console.log(a);
    }
    return B();
}
A();//3

试着依次把函数B里面的a拿掉,把函数A里面的a拿掉,控制台会输出什么?

二.作用域链

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

var a = 1
function A(){
    function B(){
        console.log(a);
    }
    return B();
}
A();//1

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

    一阵理论猛如虎,一做题目十错五!!!看题!

题目一:

var a=10;
function A(){ 
alert(a);
};
function B(){
 var a=20;
 A();
}
B();//10

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

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

var a=10;
function A(a){
  alert(a);
};
function B(){
  var a=20;
  A(a);
}
B();//20

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

题目二:

function fn (){
    a = 8;
}
console.log(a); //报错

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

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

function fn (){
    a = 8;
}
fn();
console.log(a); //8

    那我们改成这样呢?

console.log(a);
function fn (){
    a = 8;
}
fn();
console.log(a);

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

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