用几个demo帮助理解JS作用域链和声明前置(转载)

声明前置

  • 变量的声明前置:指在变量被定义时,会在代码执行之前将变量放在最前面进行初始化。
  • 函数的声明前置:会将函数声明放在代码执行之前先去生成该函数,所以函数声明受到函数提升机制的影响,所以无论放在什么位置都可以被调用。

作用域

  1. 每当声明一个新的函数就进入一个新的作用域下;
  2. 函数里需要用到的变量(或函数)首先会在自身作用域下找,找不到再逐级向上层作用域去找。

作用域链查找过程伪代码

  1. 分析代码,首先可根据提升机制将变量或者函数编译在最前面;
  2. 写出作用域链查找过程伪代码,找到每个作用域的AO、Scope;
  3. 根据伪代码方可分析出具体结果。

举例说明

  1. 变量的声明前置
        console.log(a);//undefined
        var a = 1;
        console.log(a);//1
    
        /* 声明前置分析代码 */
        var a;
        console.log(a); //undefined
        a = 1;
        console.log(a);
        //变量a会声明前置,故第一次输出:undefined
        //第二次输出前变量a已经赋值,故第二次输出:1 
  2. 函数声明放在任何位置都会函数声明前置,而函数表达式只有在这个表达式执行完后才会调用改函数
        sayName('world');
        sayAge(10);
        function sayName(name){
            console.log('hello ', name);
        }
        var sayAge = function(age){
            console.log(age);
        };
    
        /* 首先还是根据函数提升机制分析代码 */
        function sayName(){...}
        var sayAge;
        sayName('world');   //输出:hello world
        sayAge(10);        //输出:sayAge is not a function
        sayAge = function(){..}

  3.     var x = 10;
        bar() ;
        function foo() {
          console.log(x);
        }
        function bar(){
          var x = 30;
          foo();
        }
    
    /* 首先根据声明前置分析代码 */
        var x;
        function foo(){
            console.log(x);
        }
        function bar(){
            var x;
            x = 30;
            foo();
        }
        x = 10;
        bar();
    
    /* 作用域链查找过程伪代码 */
        globalContext{
            AO:{
                x:10
                foo:function
                bar:function
            }
            Scope:{}
        }
        foo[[scope]] = globalContext.AO
        bar[[scope]] = globalContext.AO
    
        fooContext{
            AO:{}
            Scope:foo[[scope]] = globalContext.AO
        }
    
        barContext{
            AO:{
                x:30
            }
            Scope:bar[[scope]] = globalContext.AO
        }
    
    //输出: 10

  4.     var x = 10;
        bar(); 
        function bar(){
          var x = 30;
          function foo(){
            console.log(x) 
          }
          foo();
        }
    
    /* 作用域链查找过程伪代码 */
        globalContext{
            AO:{
                x:10,
                bar:function
            },
            Scope:{}
        }
        bar[[scope]] = globalContext.AO
    
        barContext{
            AO:{
                x:30,
                foo:function
            }
            scope:bar[[scope]] = globalContext.AO
        }
        foo[[scope]] = barContext.AO
    
        fooContext{
            AO:{}
            scope:barContext.AO
        }
    
        //输出:30

  5.     var a = 1;
    
        function fn(){
          console.log(a);
          var a = 5;
          console.log(a);
          a++;
          var a;
          fn3();
          fn2();
          console.log(a);
    
          function fn2(){
            console.log(a);
            a = 20;
          }
        }
    
        function fn3(){
          console.log(a);
          a = 200;
        }
    
        fn();
        console.log(a);
    
    /* 作用域链查找过程伪代码 */
        globalContext{
            AO:{
                a:1->200
                fn:function,
                fn3:function
            },
            Scope:{}
        }
        fn[[scope]] = globalContext.AO
        fn3[[scope]] = globalContext.AO
    
        fnContext{
            AO:{
                a:5->6->20
                fn2:function
            }
            scope:globalContext.AO
        }
        fn2[[scope]] = fnContext.AO
    
        fn3Context{
            AO:{}
            scope:globalContext.AO
        }
    
        fn2Context{
            AO:{}
            scope:fnContext.AO
        }
    
    
        //输出: undefined 5 1 6 20 200
    

    作者:饥人谷fanlelee
    链接:https://www.jianshu.com/p/9346423613a0

原文地址:https://www.cnblogs.com/liontone/p/12313223.html