let 语句是否在全局对象上创建属性?

Do let statements create properties on the global object?

在 JavaScript 中,var 声明在全局对象上创建属性:

var x = 15;
console.log(window.x); // logs 15 in browser
console.log(global.x); // logs 15 in Node.js

ES6 引入了带有 let 具有块作用域的声明的词法作用域。

let x = 15;
{
   let x = 14;
}
console.log(x); // logs 15;

但是,这些声明会在全局对象上创建属性吗?

let x = 15;
// what is this supposed to log in the browser according to ES6?
console.log(window.x); // 15 in Firefox
console.log(global.x); // undefined in Node.js with flag

Per the specification:

"let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment."

这意味着您应该能够在执行范围内访问变量,但不能在外部访问。这扩展了执行范围,超出了 function-only 或 global 的经典 JS 闭包结构。

全局定义 let 变量不应在全局上下文中公开该变量,这在 Firefox 中曾经是这种情况。实际上,您不应在全局上下文中定义变量。

Do let statements create properties on the global object?

根据spec,没有:

A global environment record is logically a single record but it is specified as a composite encapsulating an object environment record and a declarative environment record. The object environment record has as its base object the global object of the associated Realm. This global object is the value returned by the global environment record’s GetThisBinding concrete method. The object environment record component of a global environment record contains the bindings for all built-in globals (clause 18) and all bindings introduced by a FunctionDeclaration, GeneratorDeclaration, or VariableStatement contained in global code. The bindings for all other ECMAScript declarations in global code are contained in the declarative environment record component of the global environment record.

更多解释:

  • 声明式 环境记录将绑定存储在内部数据结构中。不可能以任何方式掌握该数据结构(考虑功能范围)。

  • 一个对象环境记录使用一个实际的JS对象作为数据结构。对象的每个 属性 都成为绑定,反之亦然。全局环境有一个对象环境对象,其"binding object"是全局对象。另一个例子是 with.

现在,正如引用部分所述,只有 FunctionDeclarations、GeneratorDeclarations 和 VariableStatements 在全局环境的 object 环境记录中创建绑定。 IE。只有此绑定成为全局对象的属性。

所有其他声明(例如constlet)都存储在全局环境的declarative环境记录中,不基于全局对象.

通过 let 关键字声明的变量不会在全局对象上创建可访问的属性(window 对于浏览器)。 实际上,Firefox 修复了它的行为:let v = 42; 'v' in window // false

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var 关键字,全局定义变量,或局部定义整个函数而不管块范围。

在程序和函数的顶层,与 var 不同,let 不会在全局对象上创建 属性。例如:

   var x = 'global';
   let y = 'global';
   console.log(this.x); // "global"
   console.log(this.y); // undefined

var声明的变量的作用域是其当前执行上下文,它要么是封闭函数,要么是声明的变量在任何函数之外,全局的。如果您重新声明一个 JavaScript 变量,它不会丢失它的值。例如:

var x = 1;

if (x === 1) {
  var x = 2;

  console.log(x);
  // output: 2
}

console.log(x);
// output: 2

注:不同于CC++Java, Java当您使用 var 声明变量时,脚本没有块级作用域.

正如我们之前提到的 let 允许您声明范围限于块、语句或表达式的变量它被使用了。例如:

let x = 1;

if (x === 1) {
  let x = 2;

  console.log(x);
  // output: 2
}

console.log(x);
// output: 1

这里推荐大家阅读一下Variable Scope

标准脚本:

如果在脚本的顶层声明,letvar 变量都可以在脚本文件之外访问。但是,只有 var 个变量被分配给 window 对象。看看这个代码片段作为证明:

<script>
  var namedWithVar = "with var";
  let namedWithLet = "with let";
</script>

<script>
  console.log("Accessed directly:");
  console.log(namedWithVar);        // prints: with var
  console.log(namedWithLet);        // prints: with let
  console.log("");

  console.log("Accessed through window:");
  console.log(window.namedWithVar); // prints: with var
  console.log(window.namedWithLet); // prints: undefined
</script>

Javascipt 模块:

请注意,模块是另一回事。在模块中声明的变量在全局范围内不可用:

<script type="module">
  var namedWithVar = "with var";
  let namedWithLet = "with let";
</script>

<script>
  console.log(namedWithVar); // ReferenceError
</script>

<script>
  console.log(namedWithLet); // ReferenceError
</script>

<script>
  console.log(window.namedWithVar); // prints: undefined
  console.log(window.namedWithLet); // prints: undefined
</script>