变量提升相关面试题解析

148 阅读3分钟

关于变量提升,有很多好玩的特性,让我们来了解一下

基础补充

console.log(foo); // undefined

{
	function foo() {}
}

旧版浏览器中没有块级上下文,所以 foo 能提升到全局上下文中,输出函数体。

新版浏览器有块级上下文,块级上下文中声明的函数,会提升自全局上下文,但只声明不赋值,输出 undefined。

console.log(foo); // undefined

{
	foo = 1;
}

console.log(foo); // 1

没有关键字 var,也不是函数声明,所以不会进行变量提升,执行到赋值语句时才把 window.foo = 1

图解1 - 函数渣男论

console.log(foo); // undefined

{
	console.log(foo); // function foo {...}
	foo = 1;
	function foo() {}
	foo = 2;
	console.log('里面', foo); // 2
}

console.log(foo); // 1

首先,ECG入栈,进行变量提升(为了方便,我们这边把 VOG 和 GO 放在了一起),块作用域中的 foo 函数名提升至全局,此时打印 foo 输出 undefined。

然后 EC(BLOCK) 块级上下文入栈,此时就好玩了,首先函数被提升到作用域顶部,并赋值为函数体(0X001),此时打印 foo 为函数体,不过,块作用域中函数提升,会有这样的特性,提升完会在原地留下一行 window.foo = foo 给全局做一次赋值。,然后 foo 赋值 为 1 并给到全局,又把 VO(BLOCK) 中私有的 foo 修改为 2,所以得到代码中的输出结果。

函数渣男论:首先 foo 提升到外面,正在跟全局处对象,然后镜头来到了块级上下文,又跟块级上下文谈对象,并给它买了个包(foo = 1),接下来不幸的是,被全局上下文发现了(函数声明这一行),全局上下文哭着闹着自己也要包并分手,于是 foo 给全局上下文也买了个同样的包(window.a = a), 于是跟全局上下文分手了,foo 和 块级上下文过起了甜蜜的小日子。

试着分析一下如下代码

console.log(foo); // undefined

{
	console.log(foo); // function() {2}

	function foo() {1}

	console.log(foo); // function() { 2 }

	foo = 1;

	console.log(foo); // 1

	function foo() {2} 

	console.log(foo); // 1
}

console.log(foo); // 1
{
	function foo() {

	}

	foo = 1;

	function foo() {

	}

	foo = 2;
}
console.log(foo); // 1

函数形参默认值

var x = 1;

function func(x, y = function() { x = 2 }) { 
	x = 3;
	y(); 

	console.log(x); // 2
}

func(5);
console.log(x); // 1

图解2 - 形参默认值和 var

var x = 1;

function func(x, y = function() { x = 2 }) {
	var x = 3;
	y();
	console.log(x); // 3
}

func(5);
console.log(x); // 1

两个前置条件:

  1. 如果函数使用了形参默认值「无论赋什么值,无论实参是否传递,也无理论默认值是否生效」
  2. 在函数体中,基于 let/const/var 声明过变量「不包括 function」,当然 let 和 const 的特性导致他们不能声明已经存在的变量。

如果两个条件都符合,那么函数执行会产生两个私有上下文

  • 函数 AO
  • BLOCK 并且块级私有上下文的 "上级上下文",就是函数私有上下文。

如果块级内有 var 声明的变量和函数 AO 中存在的同名变量,则会在块中代码执行之前,首先把函数上下文中的这个变量值,同步给块级上下文一份,块级上下文的作用域链是 <EC(B), EC(FUNC)>,所以块级上下文中有 x = 5,注意,整体函数内代码,会在块级上下文中执行,然后 y 默认值的函数的作用域链是 <EC(Y), EC(FUNC)>,改的是 EC(FUNC) 中的 x。

图解:

var x = 1;

function func(x, y = function() { x = 2 }) {
	var x = 3;
	var y = function() { x = 4 };
	y();
	console.log(x); // 4
}

func(5);
console.log(x); // 1