为什么delete关键字与预期相反?

时间:2012-09-01 00:27:35

标签: javascript google-chrome

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

console = 0;

将值0分配给console。然后

console // (prints `0`)

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

delete console

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

这是预期的行为吗?这在Chromium代码中实现了什么?

8 个答案:

答案 0 :(得分:12)

MDN's documentation on delete中所述:

  

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

您的delete只是简单地取消了通过原型链继承的原生属性。

有些浏览器有window继承自原始原型,如果你真的想知道那么多细节,你会查看资源以了解属性是如何继承的,但大多数它们的工作方式就像JS自己一样。

答案 1 :(得分:3)

明白了:

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


@Randomblue:由于你对如何在v8中实现感兴趣,你可以在这里检查the trunk,或浏览流血。在某个地方,你会找到一个测试目录,它有许多处理delete的文件。特别注意全局变量/属性上使用的delete:它们不能被删除,换句话说:控制台永远不会消失。我想知道为什么这个答案从投票有帮助而被接受为无用且不被接受,但是......


这很简单。 Console不是一些随机的,独立的对象。它实际上是全局对象的属性。打开您的控制台,然后输入this.console === consolewindow.console === console。当然,它记录了。

因此,感谢暗示全局变量console = 0window.console = 0几乎相同。你有点重新分配实例的属性。与普通对象的区别在于全局对象不仅仅是任何旧对象:它的属性不能被删除(某处here on 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);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将扫描继承链并返回旧的控制台实例。控制台对象从未真正消失,它只是隐藏在您自己设置的属性后面。

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

答案 2 :(得分:2)

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

"use strict";
delete console;

您将获得例外(JSFiddle)。

您可以在http://es5.github.com/#x11.4.1

详细了解如何处理此问题

答案 3 :(得分:2)

首先,这不仅仅是控制台,您可以使用每个本机属性window上的每个浏览器定义的属性执行此操作。

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

属于ECMA-Script Spec的属性可以完全覆盖&amp;删除:

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

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

这个的简单原因是控制台和所有其他本地全局函数浏览器定义的属性通过javascript但通过C ++链接到DOMWindow对象。您可以看到控制台附加到DOMWindow right hereimplementation of DOMWindow here

这也意味着窗口对象以某种方式被掩盖为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设置为任何内容时,已消失并且只能通过(具有讽刺意义的hoorray)来复活:delete console

所以,这意味着你无法删除窗口对象上的任何本机属性当它被覆盖时尝试delete window.console,它将会再次弹出事实上你能够首先覆盖它(即使在严格模式下)而没有收到任何警告(在我看来)javascript的一个关键漏洞(几乎任何页面上的setTimeout设置为0并且看到它撕裂本身除了),但正如他们在蜘蛛侠中说的那样:

  

强大的力量带来了巨大的责任

更新

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

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

delete parseInt //=> true
parseInt //=> ReferenceError

答案 4 :(得分:0)

Firefox中完全相同。

根据我自己的观察,我假设以下内容。

首先检查变量以查看它们是否与局部变量匹配,如果没有,则会检查它们是否与window.variable匹配。

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

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

答案 5 :(得分:0)

  

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

     

...

     

您可以使用delete运算符删除声明的变量   隐式但不是用var或函数声明的那些   言。

请参阅delete on MDN

编辑:

如果您真的喜欢硬核JavaScript,请参阅Understanding delete

答案 6 :(得分:-1)

您正在覆盖对象原型,然后删除覆盖的值以及剩下的内容...是原始对象,它是原型。

答案 7 :(得分:-2)

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

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

如果你真的想看到事情破裂,试试玩delete window.console

好的,这是正式的 - 我是个白痴。 ECMAScript中的一个新功能是将属性声明为dontdelete。对此感到抱歉。