美文网首页每日一题Web前端之路让前端飞
第11题- 变量提升和函数提升

第11题- 变量提升和函数提升

作者: 青天诀 | 来源:发表于2019-08-23 00:56 被阅读0次

    面试题目(腾讯):

    下面题目输出结果是什么?

    var a=2;
    function a() {
        console.log(3);
    }
    console.log(typeof a);
    

    答案解析:

    这道题目主要考察JS的变量提升和函数提升相关的知识点

    1. 变量提升

    ES6之前,JavaScript没有块级作用域(一对花括号{}即为一个块级作用域),只有全局作用域和函数作用域。变量提升即将变量声明提升到它所在作用域的最开始的部分。下面两个示例包含了变量提升的两种情况(全局作用域和函数作用域):

    示例 1

    console.log(a);
    var a = 8;
    
    // 变量提升后等价于下面代码
    var a;
    console.log(a) // undefined
    a = 8;
    

    示例 2

    var a = 8;
    function fn() {
        console.log(a);
        var a = 9;
        console.log(a);
    }
    
    // 变量提升后等价于下面代码
    
    var a = 8;
    function fn() {
        var a;
        console.log(a);
        a = 9;
        console.log(a);
    }
    

    2. 函数提升

    js中创建函数有两种方式:函数声明式和函数字面量式。只有函数声明才存在函数提升

    示例

    console.log(f1);  
    console.log(f2);  
    function f1() {}
    var f2 = function() {}
    
    // 函数提升后等价于下面代码
    
    function f1() {}
    var fn;
    console.log(f1); // function f1() {}   
    console.log(f2); // undefined  
    f2 = function() {}
    

    3. 变量提升 VS 函数提升

    上面的面试题目就属于这种类型,变量和函数同名。当两种提升都存在的情况下,结果是什么样的呢?

    var a=2;
    function a() {
        console.log(3);
    }
    console.log(typeof a);
    
    // 变量提升和函数提升后,代码变为
    
    function a() {
        console.log(3);
    }
    a=2;
    console.log(typeof a); // number
    

    为什么是这样呢?这就需要从JS的编译过程解释了。

    在JS代码执行前,会执行词法分析。所以JS运行要分为词法分析和执行两个阶段。

    函数在运行的瞬间,会生成一个活动对象Active Object,简称AO

    1. 分析形参
      1. 如果函数有形参,则给当前活动对象增加属性,赋值为undefined
    2. 分析变量
      1. 如果AO上还没有 XXX 属性,则给当前活动对象增加属性,赋值为undefined.
      2. 如果AO上有 XXX 属性,则不做任何影响。
    3. 分析函数
      1. 把函数赋值给 AO.func 属性
      2. 如果此前 func 属性已存在,则覆盖。(证明函数的优先级比较高)

    面试题目分析系过程大致如下:

    1. 分析形参
      • 因为是全局环境,可以理解为自执行函数,没有形参。
    2. 分析变量
      • 按照2.1的规则,AO.a = undefined
    3. 分析函数
      • 按照3.2的规则直接覆盖掉a属性,AO.a = function(){console.log(3)}

    4. 为什么会有提升

    Brendan Eich给出了答案:

    1. 由于第一代JS虚拟机中的抽象纰漏导致的,编译器将变量放到了栈槽内并编入索引,然后在(当前作用域的)入口处将变量名绑定到了栈槽内的变量。(注:这里提到的抽象是计算机术语,是对内部发生的更加复杂的事情的一种简化。)
    2. 函数提升就是为了解决相互递归的问题,大体上可以解决像ML语言这样自下而上的顺序问题。

    总体意思就是:变量提升是人为实现的问题,而函数提升在当初设计时是有目的的。

    补充:

    js中无论哪种形式声明(var,let,const,function,function*,class)都会存在提升现象,不同的是, var,function,function*的声明会在提升时进行初始化赋值为 undefined,因此访问这些变量的时候,不会报ReferenceError异常,而使用 let,const,class 声明的变量,被提升后不会被初始化,这些变量所处的状态被称为“temporal dead zone”,此时如果访问这些变量会抛出ReferenceError异常,看上去就像没被提升一样。

    总结:

    1. js会将变量的声明提升到js顶部执行,因此对于这种语句:var a = 2;其实上js会将其分为var a;a = 2;两部分,并且将var a这一步提升到顶部执行。
    2. 变量提升的本质其实是由于js引擎在编译的时候,就将所有的变量声明了,因此在执行的时候,所有的变量都已经完成声明。
    3. 当有多个同名变量声明的时候,函数声明会覆盖其他的声明。如果有多个函数声明,则是由最后的一个函数声明覆盖之前所有的声明。

    扫一扫 关注我的公众号【前端名狮】,更多精彩内容陪伴你!

    【前端名狮】

    相关文章

      网友评论

        本文标题:第11题- 变量提升和函数提升

        本文链接:https://www.haomeiwen.com/subject/jbmasctx.html