10

在 Chrome 中,在控制台中尝试以下操作。第一的

console = 0;

将值分配0console。然后

console // (prints `0`)

检查我们是否正确覆盖了console. 最后,

delete console

令人惊讶的是,console现在持有原始Console对象。实际上,delete关键字“复活” console,而不是消灭它!

这是预期的行为吗?这在 Chromium 代码中在哪里实现?

4

8 回答 8

12

正如MDN 的文档delete中提到的:

如果删除操作符成功,它会从对象中完全删除该属性,尽管这可能会在对象的原型上显示一个类似命名的属性。

delete只需取消隐藏通过原型链继承的本机属性。

一些浏览器window继承了原生原型,如果你真的想知道这么多细节,你可以查看源代码以查看属性是如何继承的,但大多数情况下它们的工作方式就像 JS 自己的一样。

于 2012-09-03T14:05:57.733 回答
3

知道了:

我已经设法证明控制台是全局对象的属性:只需打开控制台并输入:this.parentwindow.parent. 这将显示更完整的属性和方法列表供您使用。包括console: Console,大约下降了 2/3,就在下面chrome: Object(有趣...:))。当我记得我以某种方式设法更改了控制台本身的 CSS 规则时,我想到了这一点(在 chrome 中,不要问我是如何到达那里的,我不记得了)。
底线:console是窗口对象的一个​​属性。我认为这很好地支持了我的解释。


@Randomblue:由于您对v8 中的实现方式感兴趣,您可以在此处检查主干,或浏览流血。您会在某个地方找到一个测试目录,其中包含许多处理delete. 特别注意delete用于全局变量/属性:它们不能被删除,换句话说:控制台永远不会真正消失。我想知道为什么这个答案从被选为有帮助和被接受变成无帮助和不被接受,虽然......


这非常简单。Console不是一些随机的、独立的对象。它实际上是全局对象的属性。打开您的控制台并输入this.console === consoleor window.console === console。当然,它是真实的。

因此,由于隐含console = 0的全局变量与window.console = 0. 您正在重新分配实例的属性。与普通对象的不同之处在于全局对象不仅仅是任何旧对象:它的属性不能被删除(在 MDN 上的某个地方)。所以你的全局正在掩盖控制台对象,它仍然存在,你也失去了你的参考:

var bar = window.console;
console = 12;
bar.log(console);//logs 12, bar is now an alternative reference to the console object
delete console;//unmasks the console reference
console === bar;//true

暂时不要误以为全局对象没有原型。只需键入this.constructor.name并瞧瞧:确实出现Window了大写字母。W另一种双重检查方法是:Object.getPrototypeOf(this);or Object.getPrototypeOf(window);。换句话说,有一些原型需要考虑。像往常一样,链以Object.prototype

 Object.getPrototypeOf(Object.getPrototypeOf(window));

简而言之,这里没有什么奇怪的事情发生,而是全局对象本身的奇怪性质。它表现得好像正在进行某种形式的原型继承。查看全局对象,就好像它是这样设置的:

this.prototype.window = this;//<-- window is a circular reference, global obj has no name
this.prototype.console = new Console();//this is the global object
this.hasOwnProperty(console);//false
console = 0;//implied global

尝试访问console时,JS 会找到console您刚刚在对象实例之前设置的Console属性,并愉快地返回它的值。当我们删除它时也会发生同样的情况,第一次出现的console被删除,但原型链更高的属性保持不变。下次console请求时,JS会扫描继承链,返回old的控制台实例。控制台对象从未真正消失过,它只是隐藏在您自己设置的属性后面。

题外话,但为了完整起见:
由于全局对象的特殊性,还有比这更多的东西(对象/原型链搜索之前的范围扫描),但这是 AFAIK,本质它。
您需要知道的是(在 JS 中)没有(在 JS 中)没有(至少)1 个原型的对象。这包括全局对象。你所做的只是增加当前全局对象的实例,删除一个属性,原型再次接管。就那么简单。这就是@Peeter 在他的回答中暗示的:在严格模式下不允许隐含全局变量,因为它们修改了全局对象。正如我在这里试图解释的那样,这正是这里发生的事情。

于 2012-09-10T09:12:12.813 回答
2

窗口对象的某些属性是不可删除的。返回 True 是因为您没有在严格模式下运行。尝试以下操作(不在控制台中):

"use strict";
delete console;

你会得到一个异常(JSFiddle)。

您可以在http://es5.github.com/#x11.4.1阅读有关如何处理的更多信息

于 2012-09-01T00:36:38.347 回答
2

首先,这不仅仅是控制台,您可以使用每个本地财产上的每个浏览器定义的属性window

setTimeout = 0;
setTimeout //=> 0
delete window.setTimeout;
setTimeout //=> function setTimeout() { [native code] }

作为ECMA 脚本规范一部分的属性可以被完全覆盖和删除:

Array = 0;
Array //=> 0
delete window.Array;
Array //=> ReferenceError

您几乎可以覆盖 上window的任何属性,删除覆盖并恢复正常功能。

这样做的简单原因是控制台和所有其他本机全局函数浏览器定义的属性不是通过 javascript 而是通过 C++ 链接到 DOMWindow 对象的。您可以在此处看到附加到 DOMWindow 的控制台以及此处DOMWindow 实现

这也意味着窗口对象不知何故是一个被屏蔽为 javascript 对象的 C++ 对象窗口对象至少部分是由 C++ 定义的,它不是原型继承在发挥作用:例如:

window.hasOwnProperty('console') //=> true, console is defined directly on the window
window.__proto__.hasOwnProperty('console') // => false, the window prototype does not have a console property

此外,如果它是原型继承,以下将导致控制台返回 3:

window.__proto__.console = 3;
delete console;
console //=> still returns console;
window.hasOwnProperty('console') //=> the window still has it.

关于原型继承的属性也是如此:

window.someProp = 4;
window.__proto__.someProp = 6;
someProp //=> 4
delete someProp;
someProp //=> 6

因此,当你设置console任何东西时,它就消失了,只能通过(讽刺万岁)复活:delete console

因此,这意味着您不能删除窗口对象上的任何本机属性。尝试delete window.console当它没有被覆盖时,它会再次弹出。事实上,您可以在没有收到任何警告(在我眼中)的情况下首先覆盖它(即使在严格模式下),这是 javascript 的关键漏洞之一(将几乎任何页面上的 setTimeout 设置为 0 并看到它撕裂本身分开),但正如他们在蜘蛛侠中所说:

拥有权利的同时也被赋予了重大的责任

更新

要包含一个提示,这是特定于浏览器/引擎的实现而不是语言本身的任何要求:在 nodejs 中,删除全局对象上的引擎指定属性和 ecma-script 属性有效:

delete this.console //=> true
console //=> ReferenceError

delete parseInt //=> true
parseInt //=> ReferenceError
于 2012-09-11T15:06:50.830 回答
0

完全相同的事情发生在 Firefox 中。

根据我自己的观察,我假设如下。

首先检查变量是否匹配局部变量,如果不匹配,则检查它们是否匹配window.variable

当您设置console为 1 时,您将局部变量设置console为 1,因此任何查找都会看到它而不是window.console(仍然存在)。当您删除delete console局部变量时。console现在任何查找都console将匹配window.console。这就是为什么你会得到你得到的行为。

我假设这是基于在 Firefox 中尝试 JavaScript 解释器。而且,我对不正确的术语感到抱歉(请随意编辑),我对命名空间没有那么丰富的经验。

于 2012-09-01T00:38:58.153 回答
0

删除操作符从对象中删除一个属性。

...

您可以使用 delete 运算符删除隐式声明的变量,但不能删除使用 var 或 function 语句声明的变量。

参见deleteMDN

编辑:

如果您真的喜欢核心 JavaScript,另请参阅了解删除。

于 2012-09-01T00:46:34.650 回答
-1

发生的情况是您正在覆盖对象原型,然后删除被覆盖的值,剩下的……是原始对象,即原型。

于 2012-09-13T13:51:24.690 回答
-2

预期的行为。鲜为人知的事实是 Javascript 控制台不在浏览器的全局空间中运行,而是在其自己的匿名函数中运行。

我知道不同的浏览器处理事情的方式不同,但简而言之——删除没有按预期运行,因为它没有在全局空间中运行。

如果您真的想看到事情破裂,请尝试使用delete window.console

好吧,这是官方的——我是个白痴。ECMAScript 中的一项新功能是将属性声明为dontdelete. 对这种混乱感到抱歉。

于 2012-09-01T00:33:22.937 回答