JS作用域链和声明前置的理解

例题:

    var a = 1;
    function fn() {
        var a 
        console.log('1.'+a);//undefined
        a = 5;
        console.log('2.'+a);//5
        function fn2() {
            console.log('3.'+a);//6
            a = 20;
        }
        a++;
        fn2();
        console.log('4.'+a);//20
    }
    fn();
    console.log('5.'+a);//1

例题中的作用域链:

        //全局作用域下的变量对象
    window.scope={
        a://var a = 1的时候是1,a = 10的时候为10,
        fn:function () {
            ...
        }
    }
    //fn()作用域下的变量对象
    1.fn.scope={
        a://声明的时候是undefined,a = 5的时候是5,a++的时候是6,fn2()中a = 20赋值过后是20,
        fn2:function () {
            ...
        }
    }
    2.window.scope={
        a:1,
        fn:function () {
            ...
        }
    }
    //fn2()作用域下的变量对象
    1.fn2.scope={}
    2.fn.scope={
        a://声明的时候是undefined,a = 5的时候是5,a++的时候是6,fn2()中a = 20赋值过后是20
        fn2:function () {
            ...
        }
    }
    3.window.scope={
        a:1,
        fn:function () {
            ...
        }
    }

console.log('1.'+a):在JS中的语句var a = 1其实是两条语句var aa = 1的合并,其中语句var a由于声明前置会被提到作用域的开头,例如:

console.log(a);
var a = 1;

这个console.log(a)会输出undefined。


console.log('2.'+a):在fn()的作用域中,a已经被声明过,所以a = 5就直接将5赋值给a。


console.log('3.'+a):为什么会输出6呢,因为:

  1. 在进入fn2()的执行环境之前,有一个a++
  2. fn2()的作用域中没有声明a,在调用a这个变量的时候就会顺着作用域链往外层查找,在fn()的作用域中找到了已经被声明的a

console.log('4.'+a):不是说好的外层作用域不能访问内层吗,可是为什么fn2()中的a = 20会影响到fn()里的a呢?因为:

  • fn2()中的a = 20改变的本来就是fn()中声明过的a的值

console.log('5.'+a):在fn2()中a = 20没有var关键字,不是说在函数内没有使用var关键字直接给变量赋值会成为全局变量吗,这里的console.log输出不应该是20吗,为什么还是1呢?

  1. 首先我们看看为什么函数内没有加var关键字的变量会变成全局变量呢?
    例如:

     function fn() {
         b = 1;
     }
     fn();
    

    我们都知道,在全局作用域下的变量和函数都是window对象的属性和方法。其实在fn()被调用的时候,b = 1顺着作用域链想找到被声明的b赋值,但是无论fn()的作用域还是全局作用域都没声明b,所以解析器就做了window.b=1这样一个行为,就是给window对象加了一个b:1的名值对。

  2. 然后为什么fn2()的a = 20没有加到全局对象上呢?
    因为fn2()顺着作用域链查找的时候在fn()的作用域中找到了已经被声明过的a,所以20就直接赋值给了fn()中已经声明过的a。


你可能感兴趣的:(JS作用域链和声明前置的理解)