(面试) 彻底解决 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 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 */ -
问下面的结果和上面的有何不同?
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。在函数内部由于变量提升机制
a带var一开始就是 undefined,b不带var将向上级作用域查找,找到全局作用域下的林一一所以2处打印出来的就是undefined "林一一"。随后a =13,window.b =13,即原来b='林一一'变成了b=13,打印出13, 13,最后第4处打印处12, 13。所以结合流程图,很明显知道答案
- 问题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
-
问题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 -
经典面试题
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,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
思考题
- 腾讯的一道变量提升的面试题
var a=2; function a() { console.log(3); } console.log(typeof a); /* 输出 * number /思路:这是一道比较简单的变量提升题,JS 代码自上而下执行时,
a被赋值成 2,输出就是number型
tenxuntongming.jpg
- 再来一道面试题
console.log(fn); var fn = 2019; console.log(fn); function fn(){} /* 输出 fn(){} 2019 /思路:这也是重名下的一道面试题,在变量提升阶段
fn由变量值声明为undefined被修改定义为fn函数本身 fn(){},所以第一个输出就是fn(){},第二个输出fn由被赋值成fn=12输出12
参考
结束
感谢阅读,我是林一一,下次见