testVariable
时测试let
具有两个不同的输出。在window
对象中定义相同名称的变量时,为什么没有运行时错误? Object.defineProperty(window, 'testVariable', {
value: 22
})
let testVariable = 12
console.log(window.testVariable) // result: 22
console.log(testVariable) // result: 12
var
时,输出是相同的。 Object.defineProperty(window, 'testVariable', {
value: 22
})
var testVariable = 12
console.log(window.testVariable) // result: 12
console.log(testVariable) // result: 12
<script>
Object.defineProperty(window, 'a', {
value: 33
})
let a = 13
</script>
<script>
console.log(a) // result: 13
</script>
<script>
Object.defineProperty(window, 'a', {
value: 33
})
</script>
<script>
let a = 13
console.log(a) // Uncaught SyntaxError: Identifier 'a' has already been declared
</script>
当在顶层用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
的可写属性,然后该属性将被覆盖,并且testVariable
和window.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声明。)