题目背景
今天在 chrome devtools 中运转以下代码:
function fn (name){
if (typeof name === 'undefined'){
console.log('name:', name)
let name = 'lily'
}
}
fn()
原以为能够一般 work,现实报错:
ReferenceError: Cannot access 'name' before initialization
寻觅答案
浏览 mdn let 文档,找到以下申明:
在 ECMAScript 2015 中,let 绑定不受变量提拔的束缚,这意味着 let 声明不会被提拔到当前实行上下文的顶部。在块中的变量初始化之前,援用它将会致使 ReferenceError(而运用 var 声明变量则恰恰相反,该变量的值是 undefined )。这个变量处于从块最先到 let 初始化处置惩罚的”暂存死区“当中。
看完上面的申明,我一脸懵逼。由于以下代码能够一般运转:
function fn (name){
if (typeof name === 'undefined'){
console.log('name:', name)
// let name = 'lily'
}
}
fn() // name: undefined
也就是说,被解释掉的第四行代码对第三行是发生的影响的,既然 let 声明不会被提拔,那第四行代码是怎样影响到第三行的?
思索这个题目,我想到<<javascript忍者秘笈(第二版)>>中第5章中有对建立词法环境步骤的形貌,在page112中,有以下说法:
在块级环境中,仅查找当前块中经由历程 let 或 const 定义的变量。关于所查找到的变量。若该标识符不存在,举行注册并将其初始化为undefined。若该标识符已存在,将保存其值。
好嘛,我更懵逼了。这个意义是 let 声明在块作用域中会被提拔吧?实践是检验真理的唯一标准,在 chrome 中代码测试下:
console.log('name:', name)
let name = 'lily'
效果报错:
ReferenceError: Cannot access 'name' before initialization
这个实践没法明白申明 let 声明是不是会提拔的题目。我的唯一标准失效了。合理我一筹莫展时,倏忽考虑到翻译历程的偏差,抱着碰运气的心态查看了 mdn 文档英文版,申明以下:
let bindings are created at the top of the (block) scope containing the declaration, commonly referred to as “hoisting”. Unlike variables declared with var, which will start with the value undefined, let variables are not initialized until their definition is evaluated. Accessing the variable before the initialization results in a ReferenceError. The variable is in a “temporal dead zone” from the start of the block until the initialization is processed.
这就很清楚了。原文说的是:经由历程 var 声明的变量有初始值 undefined,而经由历程 let 声明的变量直到定义的代码被实行时才会初始化。在变量初始化前接见变量会致使 ReferenceError。
总结
在上面的文档中,实在我一向误解了中文 javascript 文档中对变量提拔的定义。
中文 javascript 文档中的变量提拔,是指:在声明变量的代码实行之前,能够举行初始化和运用;而不是指:在建立词法环境阶段是不是会建立对应的标识符。
经由历程 var 声明的变量和 let 或 const 声明的变量,在建立响应作用域的词法环境阶段,都邑注册标识符,但仅经由历程 var 声明的变量存在会变量提拔,若在经由历程 let 或 const 声清楚明了变量的(块)作用域中,先运用再声明该变量,就会抛出毛病:
ReferenceError: Cannot access 'X' before initialization
补充
不要过分依靠翻译后的文档,已更新 mdn 上这部份的中文文档。