美文网首页大前端开发你可能不知道的JavaScript JavaScript
(面试) 彻底解决 JS 变量提升的面试题 | 一题一图,超详细

(面试) 彻底解决 JS 变量提升的面试题 | 一题一图,超详细

作者: 林一一呢 | 来源:发表于2021-02-26 09:52 被阅读0次

推荐阅读地址

掘金 github

github 求 start😄😄

前言: 大家好,我是林一一,这是一篇关于变量提升的面试题及其概念,每一道题都基本使用画图的方式讲解来保证大家能理解的更深。让我们来开始阅读吧。

思维导图

siwei导图.png

变量提升机制

一.什么是变量提升?

  • 变量提升示例
/* 你应该见过下面的类似代码,那你知道这是为什么*/
console.log(a)  // undefined
var a = 10

定义:变量提升是当栈内存作用域形成时,JS代码执行前,浏览器会将带有var, function关键字的变量提前进行声明 declare(值默认就是 undefined),定义 defined(就是赋值操作),这种预先处理的机制就叫做变量提升机制也叫预定义。
在变量提升阶段:带 var 的只声明还没有被定义,带 function 的已经声明和定义。所以在代码执行前有带 var 的就提前声明,比如这里的 a 就赋值成 undefined,在代码执行过程中遇到创建函数的代码浏览器会直接跳过。

不考虑变量提升阶段的 js 运行机制相关参考JS 运行机制基础版

讲解示例

var a =12
var b = a
b = 1
function sum(x, y) {
    var total = x + y
    return total
}
sum(1, 2)
JS代码变量提升流程图.jpg

变量提升只发生在当前作用域。比如:在页面开始加载时,只有全局作用域发生变量提升,这时候的函数中存储的都是代码字符串。

PS: 函数在调用时创建执行上下文对象还有其他关键的步骤作用域创建,this指向等这些内容放在后面文章讲,这样的机制有点类似变量提升。下面的函数创建过程都会被按作类似于变量提升来理解。

二. 带 var 和不带 var 的区别

  • 全局作用域中不带var声明变量虽然也可以但是建议带上 var声明变量,不带 var 的相当于给window对象设置一个属性罢了。

  • 私有作用域(函数作用域),带 var 的是私有变量。不带 var 的是会向上级作用域查找,如果上级作用域也没有那么就一直找到 window 为止,这个查找过程叫作用域链

  • 全局作用域中使用 var 申明的变量会映射到 window 下成为属性。

    a = 12  // == window.a
    console.log(a)  // 12
    console.log(window.a) // 12
    
    var a = b =12   // 这里的 b 也是不带 var 的。
    /* 相当于*/
    var a = 12;
    b = 12
    

思考题

  1. 问下面分别输出什么?

    // 1
    console.log(a, b)
    var a =12, b ='林一一'
    function foo(){
    // 2
        console.log(a, b)
    // 3
        var a = b =13
        console.log(a, b)
    }
    foo()
    console.log(a, b)
    
    /* 输出:
        undefined undefined
        undefined "林一一"
        13 13
        12 13
    */
    
  2. 问下面的结果和上面的有何不同?

    console.log(a, b)
    var a =12, b = '林一一'
    function foo(){
        console.log(a, b)
    //  var a =b =13
        console.log(a, b)
    }
    foo()
    // 4
    console.log(a, b)
    
    /* 输出:
        undefined undefined
        12 "林一一"
        12 "林一一"
        12 "林一一
    */
    

解答

上面的思考题不知道你都对了没,下面让我来解答,详情看图

sikaotisc.jpg

思路:1处的 a, b 其实就是window下面的属性为 undefined。在函数内部由于变量提升机制 avar 一开始就是 undefined,b不带var 将向上级作用域查找,找到全局作用域下的林一一所以2处打印出来的就是 undefined "林一一"。随后 a =13,window.b =13,即原来 b='林一一' 变成了 b=13,打印出13, 13,最后第4处打印处12, 13。所以结合流程图,很明显知道答案

  1. 问题3,再来看一道,问下面答案是什么?
    a = 0
    function foo(){
        var a =12;
        b = '林一一'
        console.log('b' in window)
        console.log(a, b)
    }
    
    foo()
    console.log(b)
    console.log(a)
    
    /* 输出
        true
        12 "林一一"
        林一一
        0
    /
    

    思路:这是比较简单的一道题,需要注意的是函数内的 b 没有带 var,b 会一直向上查找到 window 下,发现 window 下也没有就直接给 window 设置了一个属性 window.b = '林一一',同理全局下的 a 也一样。

不带var例题.jpg
  1. 问题4,问下面答案是什么?和问题3有什么区别

    function foo(){
        console.log(a)
        a =12;
        b = '林一一'
        console.log('b' in window)
        console.log(a, b)
    }
    foo()
    /* 输出
        Uncaught ReferenceError: a is not defined
    /
    

    思路:问题4和问题3的主要区别在于第一个 console.log(a) 处,因为 a 不在函数作用域内,就会向上找 window 下的作用域,发现也没有就会直接抛出引用错误 ReferenceError

  2. 经典面试题

    fn();
    console.log(v1);
    console.log(v2);
    console.log(v3);
    function fn(){
        var v1 = v2 = v3 = 2019;
        console.log(v1);
        console.log(v2);
        console.log(v3);
    }
    /*输出
        2019
        2019
        2019
        Uncaught ReferenceError: v1 is not defined
    /
    

    思路:和问题4类似,不做分析

三. 等号左边下的变量提升

  • 函数左边的变量提升

    • 普通函数下变量提升示例
    print()
    function print(){
        console.log('林一一')
    }
    print()
    

    很显然上面都输出了 林一一,因为带 function 的已经进行了变量提升

    • 匿名函数下的带=的变量提升
    print()
    var print = function() {
        console.log('林一一')
    }
    print()
    /*输出
        Uncaught TypeError: print is not a function
    /
    

    思路:同样由于变量提升机制带 var 的 print 是一开始值是 undefined,所以 print() 这时还不是一个函数,所以报出 类型错误TypeError

func=left.jpg

四. 条件判断下的变量提升

  • if else 条件判断下的变量提升
    console.log(a)
    if(false){
        var a = '林一一'
    }
    console.log(a)
    /* 输出
        undefined
        undefinedN
    /
    

    在当前作用域中不管条件是否成立都会进行变量提升

ifFalse.jpg
  • if else 条件判断下函数变量提升的坑

    • 新版浏览器中,在条件判断块级作用域之外使用条件内函数
    console.log(print())
    if(true){
        function print() {
            console.log('林一一')
        }
    }
    console.log(print())
    /* 输出
        undefined
        林一一
        undefined
    /
    
    • 新版浏览器中,在条件判断块级作用域中使用条件内函数
    if(true) {
        console.log(print())    // ???
        function print() {
            console.log('林一一')
        }
    }
    console.log(print())
    /* 输出
        林一一
        undefined
        林一一
    /
    

    思路:{} 大括号属于块级作用域,在 if else 中带 function 的函数同样也会先被声明和定义所以条件判断中的 print() 可以直接使用

iftrue.jpg

思考题

  1. 题目1,if判断语句中的变量提升
    if(!("value" in window)){
        var value = 2019; 
    }
    console.log(value); 
    console.log('value' in window); 
    
    /* 输出
        undefined
        true
    /
    

    思路:和上面所说的一样,不管条件是否成立带 var 的变量提升,当前在全局作用域 value 就是 window 的属性,所以结果显而易见输出 undefined 和 true

五. 重名问题下的变量提升

  • 函数名和 var 声明的变量重名

    var fn = 12
    function fn() {
        console.log('林一一')
    }
    console.log(window.fn)
    fn()
    /* 输出
    *  12
    *  Uncaught TypeError: fn is not a function
    /
    

    思路:带 var 声明的和带 function 声明的其实都是在 window 下的属性,也就是重名了,根据变量提升的机制,JS 代码自上而下执行时此时的 fn 还只是fn = 12,所以 fn() == 12() 又是一个类型错误 TypeError

allOne.jpg
  • 变量重名在变量提升阶段会重新定义也就是重新赋值

    console.log('1',fn())
    function fn(){
        console.log(1)
    }
    
    console.log('2',fn())
    function fn(){
        console.log(2)
    }
    
    console.log('3',fn())
    var fn = '林一一'
    
    console.log('4',fn())
    function fn(){
        console.log(3)
    }
    
    /* 输出
    *   3
    *   1 undefined
    *   3
    *   2 undefined
    *   3
    *   3 undefined
    *   Uncaught TypeError: fn is not a function
    /
    

    思路:同样由于变量提升机制,fn 会被多次重新赋值最后赋值的地址值(假设为oxfffee)为最后一个函数,所以调用 fn都只是在调用最后一个函数输出都是 3, 代码执行到var fn = '林一一',所以 fn() 其实 == 林一一() 导致类型错误 TypeError

fn重名.jpg

思考题

  1. 腾讯的一道变量提升的面试题
    var a=2;
    function a() {
        console.log(3);
    }
    console.log(typeof a);
    
    /* 输出
     * number
     /
    

    思路:这是一道比较简单的变量提升题,JS 代码自上而下执行时,a 被赋值成 2,输出就是 number

tenxuntongming.jpg
  1. 再来一道面试题
    console.log(fn);
    var fn = 2019;
    console.log(fn);
    function fn(){}
    
    /* 输出
        fn(){}
        2019
    /
    

    思路:这也是重名下的一道面试题,在变量提升阶段 fn由变量值声明为 undefined被修改定义为 fn函数本身 fn(){},所以第一个输出就是 fn(){},第二个输出 fn 由被赋值成 fn=12 输出12

参考

JavaScript面试题分析之变量提升和执行上下文

结束

感谢阅读,我是林一一,下次见

相关文章

网友评论

    本文标题:(面试) 彻底解决 JS 变量提升的面试题 | 一题一图,超详细

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