将Object.defineProperty与let或var结合使用时会发生奇怪的事情

问题描述 投票:1回答:1
  1. 谁能解释为什么使用testVariable时测试let具有两个不同的输出。在window对象中定义相同名称的变量时,为什么没有运行时错误?
    Object.defineProperty(window, 'testVariable', {
      value: 22
    })

    let testVariable = 12

    console.log(window.testVariable)   // result: 22
    console.log(testVariable)          // result: 12
  1. 但是使用var时,输出是相同的。
    Object.defineProperty(window, 'testVariable', {
      value: 22
    })

    var testVariable = 12

    console.log(window.testVariable)   // result: 12
    console.log(testVariable)          // result: 12
  1. 为什么以下代码正确运行
  <script>
    Object.defineProperty(window, 'a', {
      value: 33
    })

    let a = 13
  </script>

  <script>
    console.log(a)    // result: 13
  </script>
  1. 但是以下内容引发错误。
  <script>
    Object.defineProperty(window, 'a', {
      value: 33
    })
  </script>

  <script>
    let a = 13
    console.log(a)   // Uncaught SyntaxError: Identifier 'a' has already been declared
  </script>
javascript var let defineproperty hoist
1个回答
0
投票

当在顶层用var声明变量时,一旦脚本标签开始,就将其分配为全局对象的可写属性:

console.log(Object.getOwnPropertyDescriptor(window, 'testVariable'));
var testVariable = 12;

对于用let声明的变量,情况并非如此-它们不会被放入全局对象:

console.log(Object.getOwnPropertyDescriptor(window, 'testVariable'));
let testVariable = 12;

当您使用Object.defineProperty在对象上定义已经定义的属性,并且像[]一样传递value时>

Object.defineProperty(window, 'testVariable', {
    value: 22
})

对象上存在的先前值将被覆盖。因此,在第二个代码中,您要在全局对象上定义一个名为testVariable的可写属性,然后该属性将被覆盖,并且testVariablewindow.testVariable都将得出12

相反,在您的第一个代码中,用let声明的顶级变量创建了全局标识符,但没有分配给全局对象的属性,所以

Object.defineProperty(window, 'testVariable', {

let testVariable =

指的是不同的事物。


为什么以下代码正确运行,但是以下代码会引发错误

这很有趣。根据specification,当环境准备执行新的<script>标签时,它将运行GlobalDeclarationInstantiation进行设置。它的功能包括:

1. Let envRec be env's EnvironmentRecord.
2. Assert: envRec is a global Environment Record.
3. Let lexNames be the LexicallyDeclaredNames of script.
4. Let varNames be the VarDeclaredNames of script.
5. For each name in lexNames, do
 - If envRec.HasVarDeclaration(name) is true, throw a SyntaxError exception.
 - If envRec.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
 - Let hasRestrictedGlobal be ? envRec.HasRestrictedGlobalProperty(name).
 - If hasRestrictedGlobal is true, throw a SyntaxError exception.
6. For each name in varNames, do
 - If envRec.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.

其中envRec是全球环境记录。 (环境记录或词法环境是在给定范围内哪些变量名称引用哪些值的记录。)

与您的代码一起,抛出的GlobalDeclarationInstantiation部分是5的部分:

如果envRec.HasLexicalDeclaration(name)为true,则抛出SyntaxError异常。

在您的第三个代码中,第一个脚本标记开始之前,当GlobalDeclarationInstantiation运行时,全局环境记录没有名为a的变量,因此不会引发任何错误。相反,在您的第四个代码中,当second

脚本标记启动并运行GlobalDeclarationInstantiation时,全局环境记录中已经存在一个用a声明的名为let的变量,因此HasLexicalDeclaration调用返回true,并引发错误。 (用let声明的变量创建词法声明;用var声明的变量创建var声明。)
© www.soinside.com 2019 - 2024. All rights reserved.