Javascript 中的 window 真的是全局的吗?

IT技术 javascript scope closures theory
2021-01-17 16:08:45

在浏览器中获取这段 Javascript:

<script>

console.log(window.someThing);
var x = 12;

function foo() {
   window.otherThing = x;
}

</script>

在里面foo我们可以访问window,我们都知道,但究竟是为什么呢?

  • 它是某种特殊的全局变量吗?
  • 或者“根范围”(在script标签内)是否将其作为隐式局部变量,它是否像任何其他局部变量(x如上)一样简单地“继承自闭包”

这与直接在script标记内声明的变量被设置为 的属性window有何一致?(或者不是这样?)

<script>
var x = 12;
function() {
   console.log(window.x);
}
</script>
6个回答

您可以在 ECMAscript 中访问“范围外”“自由”变量的原因是所谓的Scope chain作用域链是来自每个执行上下文的特殊属性正如之前多次提到的,上下文对象至少看起来像:

  • [[范围]]
  • 变量/激活对象
  • “这个”上下文值

每次访问上下文(例如函数)中的变量(-name)时,查找过程总是从它自己的Activation Object. 所有形式参数、函数声明和本地定义的变量 (var) 都存储在该特殊对象中。如果在该对象中未找到变量名,则搜索进入[[Scope]]-chain。每次初始化函数(-context)时,它都会将所有父上下文变量/激活对象复制到其内部[[Scope]]属性中。这就是我们所说的词法作用域这就是闭包在 ECMAscript 中工作的原因由于Global context还有一个Variable Object(更准确地说,**全局对象的变量对象是全局对象本身)它也被复制到函数[[Scope]]属性中。

这就是您可以window从任何函数中访问的原因:-)

上面的解释有一个重要的概念性结论:ECMAscript 中的任何函数都是一个闭包,这是正确的。由于每个函数至少会在其[[Scope]]属性中复制全局上下文 VO

如何在[[Scope]]语法上访问属性?
2021-03-16 16:08:45
有没有办法改变作用域链?例如,阻止函数访问全局范围?
2021-03-29 16:08:45

Javascript 中的 window 真的是全局的吗?

是的。除非您在更窄的范围内创建一个名为 window 的新变量

function foo() {
    var window;
}

在 foo 中我们可以访问 window,我们都知道,但究竟是为什么呢?

任何函数都可以访问在更广泛范围内声明的变量。那里的窗口没有什么特别之处。

@Shef直到对象?当然 window 不是 Object 的静态属性......或者是吗?
2021-03-15 16:08:45
@Bart van Heukelom:它会检查本地范围内是否有同名的变量,如果没有找到,它将向上移动直到Object.
2021-03-16 16:08:45
但是 window 所在的作用域是作用域链中最宽的作用域,还是一个特殊的全局作用域?它是如何在运行时由 Javascript 引擎在内部解决的?
2021-03-17 16:08:45
正如我所说的“没什么特别的”(好吧,这并不完全正确,因为它是默认变量,但无论如何都没有什么特别的范围)。
2021-04-07 16:08:45
@Bart van Heukelom:抱歉,打算写Window对象。
2021-04-09 16:08:45

这一切都在 ECMAScript 中定义。

global 是一个没有外部词法环境的词法环境。所有其他环境都嵌套在其中,并绑定到具有规范指定的属性的全局对象。

这将全局对象的属性置于作用域链的开头,所有其他环境都从该作用域链继承。

ES 10.2.3 全球环境

全局环境是一个独特的词法环境,它在执行任何 ECMAScript 代码之前创建。全局环境的环境记录是一个对象环境记录,其绑定对象是全局对象(15.1)。全局环境的外部环境引用为空。

在执行 ECMAScript 代码时,可能会向全局对象添加其他属性,并且可能会修改初始属性。

ES 15.1 全局对象

在控制进入任何执行上下文之前创建唯一的全局对象。

除非另有说明,否则全局对象的标准内置属性具有属性 {[[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}。

全局对象没有 [[Construct]] 内部属性;不能将全局对象用作具有 new 运算符的构造函数。

全局对象没有 [[Call]] 内部属性;不可能将全局对象作为函数调用。

全局对象的 [[Prototype]] 和 [[Class]] 内部属性的值取决于实现。

除了本规范中定义的属性之外,全局对象可能还有其他宿主定义的属性。这可能包括一个属性,其值为全局对象本身;例如,在 HTML 文档对象模型中,全局对象的 window 属性就是全局对象本身。

@Shaz:这是一个循环引用,所以它没有结束。编译器只创建一个引用。它恰好是对创建引用的原始对象的引用。这就是为什么你能做到window.window.window...像这样:var obj = {}; obj.obj = obj;只创建了一个引用,但它是对原始引用的引用,因此您可以执行obj.obj.obj.obj.obj.obj.obj === obj.
2021-03-22 16:08:45
想想看,window.window.window.window.window === window.window.window.window
2021-03-30 16:08:45
编译器什么时候知道什么时候停止,以便我们没有无限window.window.window等等......?
2021-04-09 16:08:45

它与作用域链有关

看看Nicholas C. Zakas的以下介绍(大约从 5 分钟开始)

有趣的。看起来全局范围确实只是一个常规范围,只是在最高级别。
2021-03-18 16:08:45

window 是所有 javascript 对象的基本范围,它会自动“附加”到您定义的每个变量,除非您在声明之前使用“var”,在这种情况下,变量的范围是它的局部范围(这意味着它包含在父函数,或者也是全局的,如果你在函数块之外声明你的变量)。而且window 被定义为一个 constant,也就是说你不能重新定义 window 对象(你会得到一个错误消息“type error: redeclaration of const window”)。

所以:

window.foo = 5;

它与以下相同:

var foo = 5;

或者:

function() {
foo = 5;
}

但:

function() {
var foo = 5;
}

在这种情况下,“foo”是本地的(window.foo === 未定义)

您不是以这种方式重新定义窗口,而是创建一个带有名称为“window”的参数的闭包……这是另一回事!:)
2021-04-02 16:08:45
function语法无效,需要名称。变量foo=2infunction f(){}不等于window.foo
2021-04-04 16:08:45
您可以重新定义window. (function (window) { alert(window) })(1).
2021-04-07 16:08:45