两段JavaScript代码输出为何不同?变量提升理解与结果相悖
为什么两段类似的JavaScript代码输出不同?
首先得纠正下,你贴的两段代码看起来完全一样,应该是输入时的笔误啦!结合你说的输出差异(一个10,一个0),我猜正确的两段对比代码应该是下面两种常见场景之一:
场景一:赋值位置不同
第一段(输出10)
var a = 0; function b(){ a = 10; // 直接修改全局变量a return function a(){}; // 命名函数表达式 } b(); console.log(a); // => 10
第二段(输出0)
var a = 0; function b(){ return function a(){ a = 10; // 修改的是命名函数表达式自身的a }; } b(); console.log(a); // => 0
场景二:是否有局部变量声明
第一段(输出10)
var a = 0; function b(){ a = 10; // 无局部声明,修改全局a return function a(){}; } b(); console.log(a); // => 10
第二段(输出0)
var a = 0; function b(){ var a = 10; // 声明了局部变量a return function a(){}; } b(); console.log(a); // => 0
不管是哪种场景,核心都和变量提升以及命名函数表达式的作用域绑定有关,下面逐个拆解:
先解释场景一的逻辑
第一段代码执行过程:
- 全局作用域声明
var a = 0,变量提升后全局a存在。 - 调用
b()进入函数作用域:a = 10:因为b内部没有用var/let/const声明过a,JS引擎会向上查找作用域,找到全局的a并修改为10。return function a(){}:这是命名函数表达式(NFE)。根据ES规范,NFE的名字a会被绑定到b的作用域中,但这个绑定是外部不可访问的——也就是说,b内部除了这个函数自己,没人能拿到这个a。所以之前的a=10完全不受它影响,修改的还是全局变量。
- 最后
console.log(a)读取全局变量,结果是10。
第二段代码执行过程:
- 全局同样声明
var a = 0。 - 调用
b()返回一个命名函数表达式,但我们并没有执行这个返回的函数!- 即使这个函数内部写了
a=10,也只会修改它自己作用域内的a(也就是这个函数的名字本身),和全局a无关,而且这行代码根本没机会运行。
- 即使这个函数内部写了
- 最后读取全局
a,自然还是0。
再解释场景二的逻辑
第二段里b内部多了var a = 10:
var a会被变量提升,在b的作用域内创建一个局部变量a,优先级高于全局a。a=10修改的是这个局部a,和全局变量完全无关。- 后面的
return function a(){}作为命名函数表达式,会在b的作用域内创建一个新的不可访问的a绑定,但不管怎样,全局a自始至终没被碰过,所以最后输出0。
关键知识点总结
- 变量提升:
var声明的变量和函数声明会被提升到作用域顶部,但命名函数表达式不会被提升,它的名字仅在自身内部(或当前函数作用域的隐藏绑定)有效。 - 命名函数表达式的特性:NFE的名字会在包裹它的函数作用域中创建一个不可外部访问的绑定,同时在自身内部可访问——这是它和普通函数表达式、函数声明的核心区别。
- 作用域查找规则:JS引擎查找变量时,会从当前作用域向上遍历,找到第一个匹配的变量就停止。
内容的提问来源于stack exchange,提问作者SnapADragon
相关产品推荐
相关产品推荐

