Javascript 中删除运算符的目的是什么?

IT技术 javascript
2021-03-02 17:08:47

delete 操作符的行为看起来非常复杂,而且对于它的实际作用存在很多误解对我来说,似乎重新分配某些东西undefined会更可靠地完成您的期望。

我从未delete在非示例代码中实际使用过 Javascript 中关键字,我想知道它是否对任何东西都特别有用。是否delete有任何不能通过重新分配到 来实现的目的undefined它是否在任何著名的库(例如 jQuery、dojo、backbone 等)中使用过?

6个回答

删除是否有任何不能通过重新分配给 undefined 来实现的目的?

是的。如果你想从一个原型或造成财产揭露inhasOwnProperty以及for (...in...)不记录财产作为现有然后delete是适当的。

let set = {};

set._x = true;

alert('_x' in set);  // true

set._x = undefined;

alert('_x' in set);  // true

delete set._x;

alert('_x' in set);  // false

编辑:正如 TJ Crowder 解释的那样:

delete运算符的目的是从对象中完全删除属性,而将属性设置为undefined仅将属性设置为undefined

这本身很重要,但在使用继承时也很重要,因为如果 O 派生自 P

let P = { prop: 42 };
let O = Object.create(P);  // P is O's prototype.

当您检索 时O.prop,如果 O 具有具有该名称的属性(即使其值未定义),您将从 O 获取 prop 的值,但如果 O 根本没有该属性,则将从中检索该值P.prop

console.log(O.prop);  // "42" since O doesn't have its own prop, but P does.
O.prop = undefined;
console.log(O.prop);  // "undefined" since O has its own prop.
delete O.prop;
console.log(O.prop);  // "42" since the delete "unmasked" P.prop.

正如 Mike Samuel 在他的回答中指出的那样,delete 最常见的用法之一是当您将对象视为将名称与值相关联的“属性包”时。“这个名字现在被映射到某个虚假值”和“这个名字根本没有被映射”在逻辑上是有区别的。“删除”实现了后者。

这一切都很好理解。我想我可以添加一个关于 JScript 1.0 到 5.0 引擎的有趣历史注释。

在 JScript 的那些原始 Microsoft 实现中,我们使用 OLE 自动化样式的 IDispatch 对象来实现 expando 对象。IDispatch 当然是通过将名称与“调度 ID”相关联来工作的,“调度 ID”只是一个整数。要动态调用,首先您要求调度对象为您提供与名称关联的调度 ID,然后您说“现在调用与此 ID 关联的方法,给定这些参数”。

这一切都很好。但是 IDispatch 契约的要求之一是从名称到调度 ID 的映射在对象的整个生命周期内保持稳定因此,如果有人说“将属性 Foo 添加到此对象”,那么我们可能会决定属性 Foo 与该对象中的调度标识符 0x1234 相关联。从那一刻起,每次向对象询问“Foo”的调度标识符时,它必须返回 0x1234,即使 Foo 被删除并随后再次添加。这允许调用者维护他们自己的名称/dispid 对的快速缓存,而不是每次调用时都必须询问对象。

其实际结果是“删除”不会以任何方式减轻该实现中对象的内存负担当您删除一个属性(在原始实现中)时,我们必须向对象中添加一点,将调度标识符标记为已删除,但我们必须保留有关名称/ID 配对的所有信息,以防该名称再次出现。向对象添加大量属性然后删除所有属性不会缩小内存中的对象。

JScript 引擎当然从我那个时代开始就被完全重写了(我相信,除了解析器和词法分析器),所以我不知道引擎是否仍然有这个不寻常的怪癖。找出答案会很有趣。

如果你这样做

 delete Foo.Bar;

它从对象 Foo 中完全删除了属性 Bar

 Foo.Bar = undefined

只是将 Bar 属性设置为 undefined 并且Foo.Bar仍然存在

其他答案正在解释delete关键字背后的动机我想补充一点,截至 2017 年,浏览器在删除属性和将属性设置为未定义时都会释放内存

考虑这个例子(来源roughSizeOfObject()):

> var obj = {a:42,b:"b"}; roughSizeOfObject(obj)
26
> obj.a = undefined; roughSizeOfObject(obj)
18
> delete obj.a; roughSizeOfObject(obj)
10
> obj.b = undefined; roughSizeOfObject(obj)
8
> delete obj.b; roughSizeOfObject(obj)
0

该示例来自 Chrome 61(64 位)控制台(请注意,其中的所有字符都在String内部编码为 16 位无符号整数)。

@Vishal-Lia 我想说 MDN 在这方面可能已经过时了。
2021-05-05 17:08:47
可能 MDN 是对的。在roughSizeOfObject() 的实现中,字节大小是硬编码的,具体取决于类型以及该属性是否在对象中可用。而且我认为垃圾收集器稍后会释放内存。如果我错了,请纠正我。
2021-05-19 17:08:47
但是在 MDN 站点中提到“与普遍的看法不同,删除操作符与直接释放内存无关。内存管理是通过破坏引用间接完成的,有关更多详细信息,请参阅内存管理页面。”。那么哪个是真的?
2021-05-21 17:08:47

您可以查看以下链接的答案Can I set variables to undefined or pass undefined as an argument? 这以非常详细的方式解释了差异。

概括:

您当然可以将 undefined 分配给它,但这不会删除变量。只有 delete object.property 操作符才能真正删除东西。

delete 实际上是用于属性而不是变量本身。浏览器会让你直接删除变量,但这不是一个好主意,并且在 ECMAScript Fifth Edition 的严格模式下不起作用。如果你想释放对某物的引用以便它可以被垃圾收集,通常说 variable=null 会更常见。