让语句在全局对象上创建属性吗?

IT技术 javascript ecmascript-6 let
2021-02-07 09:23:07

在 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
5个回答

不要let语句创建全局对象的属性?

根据规范,没有:

全局环境记录在逻辑上是单个记录,但它被指定为封装了对象环境记录声明性环境记录的复合对象环境记录作为其基本对象相关的全局对象境界这个全局对象就是全局环境记录的GetThisBinding具体方法返回的值全局环境记录的对象环境记录组件包含所有内置全局变量(第 18 条)的绑定以及全局代码中包含FunctionDeclarationGeneratorDeclarationVariableStatement引入的所有绑定全局代码中所有其他 ECMAScript 声明的绑定包含在全局环境记录声明性环境记录组件中。

还有一些解释:

  • 声明环境记录存储在内部数据结构中的绑定。以任何方式获取该数据结构都是不可能的(想想函数作用域)。

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

现在,正如引用的部分所述,只有FunctionDeclaration s、GeneratorDeclaration s 和VariableStatement s 在全局环境的对象环境记录中创建绑定即只有 this 绑定成为全局对象的属性。

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

但是 8.1.1.1 ( people.mozilla.org/~jorendorff/... ) 表明letvar创建相同类型的环境记录(即它们都创建declarative environment records),表明letvar全局对象上的位置可能对应。然而,我想,全球案例可能是特殊的。
2021-03-17 09:23:07
let并且var不要创建记录。letvar绑定“存储”在环境记录中。并且有不同类型的环境记录:声明性(您链接到的内容)、对象、函数、module和全局记录,其中声明性和对象记录是两种主要类型(所有其他类型都是派生类)。
2021-03-18 09:23:07
Felix,您能否确认VariableStatement(在您的报价中提到)意味着专门的函数范围声明,还是也包括在内let我认为是前者。
2021-03-21 09:23:07
澄清一些事情:声明性记录和对象记录之间的最大区别在于声明性记录使用内部数据结构来存储绑定。对象记录使用实际的 JS 对象作为“存储”。在 ES5 中,全局环境被定义为对象记录(es5.github.io/#x10.2.3),这就是为什么全局范围内的所有变量声明都成为全局对象的属性。然而在 ES6 中,全局环境由两种不同的环境记录类型组成,并且只有var声明存储在对象记录中,let声明不是。
2021-03-30 09:23:07
请你能澄清你做出区分的目的吗?如果我在全局范围内声明一个变量,则会创建一个环境记录(并且该变量像您说的那样存储在其中)。所以我们都是“正确的”不是吗?您是说使用的环境记录类型取决于我们是否在全局范围内?
2021-04-04 09:23:07

标准脚本:

无论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 module:

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

<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>

根据规范

“let 和 const 声明定义了作用域为运行执行上下文的 LexicalEnvironment 的变量。”

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

let全局定义一个变量可以解释这一点,正如您在 Firefox 中看到的那样,它绑定了一个全局变量,而 V8/iojs 则没有。

值得一提的是,console.log(typeof x)number在iojs中返回。在实践中,你不应该在module之外定义变量,或者尽可能多地定义函数......尤其是constlet

Firefox 似乎已将其行为更改为与 V8 一致,不再将 let 和 const 声明绑定到全局范围。
2021-03-12 09:23:07
“在全局范围内定义一个 let 变量使这有待解释”正如您从我的回答中看到的那样,事实并非如此。你说的是对的,我们确实有更多的东西可以创建范围(还有module),但是,仅此而已并不能回答提出的问题。
2021-03-23 09:23:07

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

let允许您声明范围仅限于使用它的块、语句或表达式的变量。这与var关键字不同,它定义全局变量或局部变量,而不管块范围如何。

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

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

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

var x = 1;

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

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

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

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

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

let x = 1;

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

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

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

在这里,我建议您阅读有关Variable Scope 的内容