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

推荐阅读地址

掘金 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面试题分析之变量提升和执行上下文

结束

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352