资讯专栏INFORMATION COLUMN

ReferenceError: Cannot access 'X' before

Elle / 4794人阅读

摘要:中文文档中的变量提升,是指在声明变量的代码执行之前,可以进行初始化和使用而不是指在创建词法环境阶段是否会创建对应的标识符。

问题背景

今天在 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 声明不会被提升,那第四行代码是怎么影响到第三行的?
思考这个问题,我想到<>中第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 上这部分的中文文档。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104262.html

相关文章

  • [译]Understanding javascript&#039;s &#039;undefined

    摘要:一个表示编译器检测到一个无效的引用值。在实际情况中,往往是在获取一个未被赋值的引用时被抛出。任何一个函数上下文都有一个被称为活动对象的变量对象。没有找到的话,就会认为引用名没有基础值并抛出的错误。下没有下的属性仅存在于被启动的情况下。 和其他语言相比,javascript中的对于undefined的理解还是有点让人困惑的。特别是试着理解ReferenceErrors错误(x is no...

    galaxy_robot 评论0 收藏0
  • Useful APIs that you probably don&#039;t notice

    摘要:结果 Date Get the number of days in a month The 0th day of next month is the last day of the current month. function daysInMonth(year, month) { let date = new Date(year, month + 1, 0); ret...

    崔晓明 评论0 收藏0
  • local variable &#039;var1&#039; referenced before

    摘要:起初是群里一个哥们这句话报错。我竟然没看懂代码如下其实这个代码不能很好的反应问题。来看以下两个我一开始没理解这个问题。看到和粗略的理解成不能够在闭包函数中改变上层函数的变量。实际上是,如果一个变量被赋值,那么会认为其为局部变量。 起初是群里一个哥们这句话报错。后来之前的一个实习生,给出了链接来解释这个问题。 我竟然没看懂.... http://stackoverflow.com/que...

    Aklman 评论0 收藏0
  • antd报错Cannot read property &#039;filter&#039; of u

    摘要:技术栈问题描述在状态组件中书写下的其中涉及点击某处出现弹框的操作,在中有选框,点击取消或清空所有表单信息。点击选框内的各个选项,报错。在点击取消之后,清空的是所有组件的内容,再打开新的弹窗后,组件的状态并没有刷新。 技术栈: react + dva + antd 问题描述: 在状态组件中书写state下的columns,其中涉及点击某处出现弹框modal的操作,在modal中有sel...

    Tychio 评论0 收藏0
  • webpack编译报错Cannot find module &#039;@babel/core�

    摘要:编译报错用了这个指令后报错尝试重新下载了还是不行原来是被我更新了不支持原来的配置了,官方默认对应的版本需要一致即需要搭配最新版本两种解决方案回退低版本更新到最高版本编译无法识别语法需要安装之后 ** webpack编译报错Cannot find module @babel/core **~ npm install babel-core babel-loader --save-dev ...

    DandJ 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<