为何三个涉及函数提升的JavaScript示例输出不同?
解答:块级作用域内函数声明的特殊行为
这三个示例的核心差异,源于ES6规范对块级作用域(比如if块)内函数声明的特殊处理规则——它不是简单的「提升到全局」,而是分两步影响块内和全局的同名变量,且函数声明在块内的位置直接决定了全局变量被修改的时机和值。
先明确关键规则
对于块内的function a() {}声明,JS引擎会执行以下逻辑:
- 块级绑定创建:进入块时,立刻在块级作用域创建一个和函数同名的变量(效果类似
let a),并将它初始化为全局作用域中同名var变量的当前值。 - 函数声明执行:当代码运行到
function a() {}这一行时:- 把块级作用域的a赋值为函数本身;
- 同时把全局作用域的var a赋值为执行这行代码前块级a的当前值。
- 块结束后,块级作用域的a被销毁,全局a保留最后一次被修改的值。
接下来逐个拆解三个示例:
示例1:函数声明在a=1之后,a=5之前
var a = 0; console.log("1 a:", a); // 全局a=0,输出0 if(true){ a = 1; // 块级a初始为全局a的0,这里修改块级a为1 function a() {} // 执行声明:①把全局a设为块级a当前的1;②把块级a设为函数 a = 5; // 修改块级a为5 console.log("2 a:", a); // 块级a=5,输出5 } console.log("3 a:", a); // 全局a已经被设为1,输出1
核心:函数声明执行时,块级a的值是1,所以全局a被改成1;后续块内修改块级a不影响全局。
示例2:函数声明在块的最后
var a = 0; console.log("1 a:", a); // 全局a=0,输出0 if(true){ a = 1; // 块级a初始为0,改为1 a = 5; // 块级a改为5 console.log("2 a:", a); // 块级a=5,输出5 function a() {} // 执行声明:①把全局a设为块级a当前的5;②把块级a设为函数 } console.log("3 a:", a); // 全局a被改成5,输出5
核心:函数声明执行时,块级a已经被改成5,所以全局a被同步为5。
示例3:函数声明在块的最开头
var a = 0; console.log("1 a:", a); // 全局a=0,输出0 if(true){ function a() {} // 执行声明:①把块级a设为函数;②把全局a也设为这个函数 a = 1; // 修改块级a为1(不影响全局的函数a) a = 5; // 修改块级a为5 console.log("2 a:", a); // 块级a=5,输出5 } console.log("3 a:", a); // 全局a被设为函数,输出ƒ a() {}
核心:函数声明执行时,直接把全局a替换为函数;后续块内修改的是块级a,和全局a无关。
总结问题答案
- 函数提升的语境差异:三个示例的函数提升都发生在块级作用域内,但函数声明在块内的位置不同,导致全局var变量被同步修改的时机和值完全不同——本质是块内函数声明会在执行到它的位置时,将块级变量的当前值同步给全局同名var变量。
- 输出不同的原因:
- 示例1:函数声明在
a=1后执行,全局a被同步为1; - 示例2:函数声明在
a=5后执行,全局a被同步为5; - 示例3:函数声明在最开头执行,全局a被直接替换为函数,后续块内修改不影响全局。
- 示例1:函数声明在
内容的提问来源于stack exchange,提问作者klion
相关产品推荐
相关产品推荐

