为什么delete关键字的作用与预期相反

Why does the delete keyword act opposite to expected?

本文关键字:作用 delete 关键字 为什么      更新时间:2023-09-26

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

console = 0;

以将值CCD_ 1分配给CCD_。然后

console // (prints `0`)

以检查我们是否正确地覆盖了CCD_ 3。最后,

delete console

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

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

如MDN关于delete:的文档中所述

如果delete运算符成功,它将从对象,尽管这可能会显示一个类似名称的属性在对象的原型上。

您的delete只是取消了通过原型链继承的Windows本机属性。

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

得到了:

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


@Randombue:由于您对v8中的实现方式感兴趣,您可以在此处查看主干,或浏览出血。在某个地方,您可以找到一个测试目录,其中包含许多处理delete的文件。特别注意全局变量/属性上使用的delete:它们无法删除,换句话说:控制台从未真正消失。我想知道为什么这个答案从被投票支持和接受变成了没有帮助和不被接受,尽管。。。


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

因此,由于隐含的全局变量,console = 0window.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,您就会发现:确实会出现带有大写WWindow。另一种双重检查方式是: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会找到您刚刚在console1对象的实例之前设置的属性console,并愉快地返回其值。当我们删除它时也会发生同样的情况,第一次出现的console被删除,但原型链上更高的属性保持不变。下一次请求console时,JS将扫描继承链并返回old的控制台实例。控制台对象从未真正消失,它只是隐藏在您自己设置的属性后面。

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

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

"use strict";
delete console;

并且您将得到一个异常(JSFiddle)。

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

首先,这不仅仅是控制台,您可以使用

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链接到DOMWindow对象,而是通过C++链接到DOMWindows对象。你可以在这里看到连接到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设置为任何值时,它就消失,并且只能通过(讽刺的是hoorray)复活:delete console

因此,这意味着不能删除窗口对象上的任何本地属性尝试delete window.console,当它没有被覆盖时,它只会再次弹出。事实上,你可以在一开始就覆盖它(即使是在严格模式下),而不会收到任何形式的警告(在我看来),这是javascript的关键漏洞之一(将几乎任何页面上的setTimeout设置为0,然后看到它自行撕裂),但正如他们在spiderman中所说:

伟大的力量带来伟大的责任

更新

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

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

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

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

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

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

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

delete运算符从对象中删除特性。

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

参见MDN 上的delete

编辑:

如果你真的很喜欢核心JavaScript,请参阅理解删除。

发生的情况是覆盖对象原型,然后删除覆盖的值和剩下的。。。是原始对象,这是它的原型。

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

我知道不同的浏览器处理事情的方式不同,但简而言之,delete并没有像预期的那样运行,因为它不是在全局空间中运行的。

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

好吧,这是官方的——我是个白痴。ECMAScript中的一个新特性是能够将属性声明为dontdelete。很抱歉造成这种混乱。