javascript 的变量提升与函数提升、执行上下文、执行上下文栈及相关面试题解:
Ⅰ、变量提升与函数提升:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>01_变量提升与函数提升</title>
</head>
<body>
<!--
1、变量声明提升:
* 通过 var 定义(声明)的变量,在定义语句之前就可以访问到;
* 值为:undefined
2、函数声明提升:
* 通过 function 声明的函数,在之前就可以直接调用
* 值为:函数定义(对象)
注意:无论是变量声明提升还是函数声明提升,都是一个现象结果(核心是:如何产生或导致了该现象结果);
3、问题:变量提升和函数提升是如何产生的?
-->
<script type="text/javascript">
console.log("----------------")// 此处可以作为 window 打印定位的锚点;
/*
面试题:输出 undefined
*/
var a = 3
function fn() {
console.log(a)// undefined,原因:fn 函数内部有 a 变量的声明,因此不调用函数外声明的 var a = 3,而 fn 函数内的 var a = 4 语句只是声明了但没赋值,因此输出为 undefined;
var a = 4
}
fn()
console.log(b)// undefined,原因:变量提升;
fn2()// 'fn2()',可调用,原因:函数提升(使用声明方式来声明函数);
fn3()// Uncaught TypeError: fn3 is not a function,不能调用,原因:变量提升(注意此时遵循的是变量提升而不是函数提升,因为此时不是使用声明的方式声明函数);
var b = 3
function fn2() {
console.log('fn2()')
}
var fn3 = function() {
console.log('fn3()')
}
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

Ⅱ、执行上下文:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>02_执行上下文</title>
</head>
<body>
<!--
1、代码分类(根据位置来分类)
* 全局代码
* 函数(局部)代码
2、全局执行上下文:
* 在执行全局代码前将 window 确定为全局执行上下文
* 对全局数据进行预处理
* var 定义的全局变量 ==> undefined,添加为 window 的属性;
* function 声明的全局函数 ==> 赋值(fun,即:函数对象),添加为 window 的方法;
* this ==> 赋值(window 对象)
* 开始执行全局代码
3、函数执行上下文:
* 在调用函数,准备执行函数体之前,创建对应的函数执行上下文对象(虚拟的,存在于栈中);
* 对局部数据进行预处理
* 形参变量 ==> 赋值(实参) ==> 添加为执行上下文的属性
* arguments ==> 赋值(实参列表),添加为执行上下文的属性
* var 定义的局部变量 ==> undefined,添加为执行上下文的属性
* function 声明的函数 ==> 赋值(fun,即:函数对象),添加为执行上下文的方法
* this ==> 赋值(调用函数的对象)
* 开始执行函数体代码
-->
<script type="text/javascript">
// 全局执行上下文:
c = 3// 不能在执行该语句之前添加为 window 的属性:因为不是通过 var 来定义的全局变量(注意:只有通过 var 定义的全局变量才能进行预处理,才能添加到 window 对象上);
// 而非 var 定义的全局变量并没有报错(因为语法允许),但只有在执行该 c = 3 语句后,才能添加到 window 对象上;
console.log(a1)// undefined,本质是:window.a1
console.log(a2)// ƒ a2() { console.log('a2()') },本质是:window.a2
console.log(a2())// a2(),undefined,本质是:window.a2()
// 输出 a2() 的原因:调用了 a2 函数、输出 undefined 的原因:该 a2 函数没有返回值,因此输出为 undefined;
console.log(this)// window 对象
var a1 = 3
function a2() {
console.log('a2()')
}
console.log('a1')// a1
console.log('----------------------------------------------------')
// 函数执行上下文:
function fn(a1) {
console.log(a1)// 2
console.log(a2)// undefined
a3()// 'a3()'
console.log(this)// Window 对象
console.log(arguments)// (2,3)(即:伪数组,Arguments 对象)
var a2 = 3
function a3() {
console.log('a3()')
}
}
fn(2,3)
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

Ⅲ、执行上下文栈:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>03_执行上下文栈1</title>
</head>
<body>
<!--
1、在全局代码执行前,JS 引擎就会创建一个栈来存储管理所有的执行上下文对象;
2、在全局执行上下文(window)确定后,将其添加到栈中(压栈);
3、在函数执行上下文创建后,将其添加到栈中(压栈);
4、在当前函数执行完后,将栈顶的对象移除(出栈);
5、当所有的代码执行完后,栈中只剩下 window;
6(*)、执行上下文的个数的确定:函数调用的个数 + 全局执行上下文(即:N + 1)
-->
<script type="text/javascript">
// 代码产生过三个执行上下文对象:Window、bar(10) 函数调用、foo(x + b) 函数调用(注意:只有函数被调用才会产生函数执行上下文对象,函数仅声明并不会产生函数执行上下文对象);
var a = 10
var bar = function(x) {
var b = 5
foo(x + b)// 此时能成功执行的原因:不能遵循变量提升原则,因为此时的 foo(x + b) 函数执行是在 foo 函数完全声明后,因此此时并不会报错;
}
var foo = function(y) {
var c = 5
console.log(a + c + y)// 30
}
bar(10)
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

其三、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>04_执行上下文栈2</title>
</head>
<body>
<!--
1、依次输出什么?
global begin: undefined
foo() begin: 1
foo() begin: 2
foo() begin: 3
foo() end: 3
foo() end: 2
foo() end: 1
global end: 1
2、整个过程中产生了几个执行上下文?
N + 1 ==> 4 + 1 ==> 5
-->
<script type="text/javascript">
console.log('global begin: ' + i)
var i = 1
foo(1)
function foo(i) {
if(i == 4) {
return
}
console.log('foo() begin:' + i)
foo(i + 1)// 递归调用:在函数内部调用自己
console.log('foo() end:' + i)
}
console.log('global end: ' + i)
</script>
</body>
</html>
其四、结果为:
// 一进入页面后的控制台:

其五、执行上下文栈的流程分析:
// 图一是案例的流程分析,图二是执行上下文逻辑分析;


Ⅳ、面试题:
其一、代码为:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>05_面试题</title>
</head>
<body>
<script type="text/javascript">
/*
测试题1:核心问题:变量提升与函数提升,谁先提升?
*/
// 答:先执行变量提升,再执行函数提升;
function a() {}
var a
console.log(typeof a)// 'function'(先执行变量提升,再执行函数提升,因此输出结果为:function,反过来则输出 undefined)
/*
测试题2:
*/
if(!(b in window)) {// 判断在 window 对象中,是否存在 b 属性
var b = 1
}
console.log(b)// undefined(说明:b 存在变量提升,b in window 为 true,因此输出结果为 undefined)(注意:此时是 if(){} 语句,而非函数内部)
// 只能存在的两种结果:undefined(说明 var b = 1 没有执行过)、1 (说明 var b = 1 执行过)
/*
测试题3:
*/
var c = 1
function c(c) {
console.log(c)
}
console.log(this)
c(2)// Uncaught TypeError: c is not a function
// 报错点:先执行变量提升,再执行函数提升,因此在函数提升过后就在 window 中存在了 c 函数,因此再给 c 变量赋值的时候就报错了( c = 1 就会报错),因此还没执行 c(2) 语句就报错了;
// 核心点:报错点没问题,但是在调用 c(2) 的过程中,c 就不再是一个函数了,因为已经有 c 的变量声明;
</script>
</body>
</html>
其二、结果为:
// 一进入页面后的控制台:

Ⅴ、小结:
其一、哪里有不对或不合适的地方,还请大佬们多多指点和交流!
其二、若有转发或引用本文章内容,请注明本博客地址(直接点击下面 url 跳转) https://blog.csdn.net/weixin_43405300,创作不易,且行且珍惜!
其三、有兴趣的话,可以多多关注这个专栏(Vue(Vue2+Vue3)面试必备专栏)(直接点击下面 url 跳转):https://blog.csdn.net/weixin_43405300/category_11525646.html?spm=1001.2014.3001.5482
其四、再有兴趣的话,也可以多多关注这个专栏(Java)(直接点击下面 url 跳转):https://blog.csdn.net/weixin_43405300/category_12654744.html?spm=1001.2014.3001.5482