JavaScript中if块内var与function声明的作用域差异及执行结果疑问
搞懂ES6块级函数的诡异行为:为什么代码块2和3结果不一样?
嘿,作为JavaScript新手碰到这个问题太正常了——这玩意儿确实是ES6里为了兼容旧代码搞出来的“坑”,咱们一步步拆解,把它说清楚。
先抓核心:非严格模式下的块级函数是个妥协产物
首先你要知道:ES6引入块级作用域后,非严格模式下的块级函数声明行为是怪异的,完全是为了兼容ES5之前的未定义行为。而严格模式下的行为就很清晰,和let/const的块级作用域一致。咱们先聚焦你写的非严格模式代码。
拆解代码块2的执行逻辑
先看你的代码块2,我把它的执行步骤拆成大白话:
function checkShape() { var shape = 'square'; console.log(`'shape' variable got declared in checkShape Scope`,shape); // 输出'square' var shape = 'rectangle'; console.log(`'shape' variable got reassigned value 'rectangle' in checkShape Scope`,shape); // 输出'rectangle' if(true){ console.log(`'shape' function declaration got hoisted in checkShape Scope`,shape); // 输出函数体 function shape() {}; console.log(`'shape' function declaration found in checkShape Scope`,shape); // 输出函数体 shape = 'triangle'; console.log(`'shape' function declaration got reassignment in if Scope`,shape); // 输出'triangle' } console.log(`'shape' function found in checkShape Scope`,shape); // 输出函数体 } checkShape();
- 函数提升的特殊规则:非严格模式下,块里的
function shape()会被偷偷提升到整个checkShape函数的顶部,相当于你在函数最开头就写了function shape() {}。 - 进入块时的“劫持”:当进入
if块的瞬间,ES6会把外层的shape变量强制替换成这个提升的函数——不管你块里的函数声明写在哪个位置,只要它在块里,进入块就触发这个替换。 - 临时赋值不影响外层最终值:你在块里把
shape改成'triangle',只是临时修改了这个被劫持的变量,但离开块时,ES6会自动把shape恢复成进入块之前的那个函数(这是兼容旧代码的奇葩逻辑)。
所以最后外层的shape又变回了函数体。
再看代码块3:顺序变了,逻辑全变
代码块3把shape = 'triangle'和function shape()换了顺序,执行逻辑完全不同:
function checkShape() { var shape = 'square'; console.log('1',shape); // 'square' var shape = 'rectangle'; console.log('2',shape); // 'rectangle' if(true){ console.log('3',shape); // 输出'rectangle' shape = 'triangle'; console.log('4',shape); // 'triangle' function shape() {}; console.log('5',shape); // 'triangle' } console.log('6',shape); // 'triangle' } checkShape()
- 进入块时没有立刻劫持:因为
function shape()写在赋值之后,进入块时不会马上把外层的shape换成函数,此时shape还是外层的'rectangle',所以第三个log输出'rectangle'。 - 赋值先覆盖,函数声明失效:你先把
shape改成'triangle',等执行到function shape()那一行时,非严格模式下会认为“变量已经被赋值成非函数类型了,这个函数声明就不生效了”——相当于这行代码直接被忽略了。 - 没有触发恢复逻辑:因为函数声明的赋值没生效,离开块时ES6不会恢复
shape的值,所以外层的shape保持为'triangle'。
给你的新手建议
- 立刻开启严格模式:在代码最顶部加
'use strict';,这样块级函数的行为就和let一致了——完全是块作用域,不会提升到外层,也不会有奇葩的恢复逻辑,再也不会绕晕。 - 用
const声明函数代替块级函数:比如把function shape() {}改成const shape = () => {}或者const shape = function() {},行为完全符合预期,没有歧义。 - 避开非严格模式的坑:非严格模式的这些怪异行为都是历史遗留问题,现在写代码没人用非严格模式了,直接用严格模式+ES6标准语法就行。
内容的提问来源于stack exchange,提问作者Raj Thakar
相关产品推荐
相关产品推荐

