最近想重新系统地整理一下前端的知识,因此写了这个专栏。我会尽量写一些业务相关的小技巧和前端知识中的重点内容,核心思想。
前言
JavaScript这门语言,在设计上其实存在则很多模糊的地方。同时,在高校中也没有专门的课程会带大家熟悉这门语言。导致很多的使用技巧其实是由工作了一段时间的大神们总结出来的。诸如创建一个干净的对象,我们更推荐用 Object.create(null)等。可是很少会听到别人说delete操作符的问题。今天我们就来探讨一下,应该不应该有delete操作符删除对象属性。
delete操作符
在MDN的网站中是这样描述delete的: delete 操作符用于删除对象的某个属性;如果没有指向这个属性的引用,那它最终会被释放。
它的使用方式如下:
const obj = {};
console.log(obj.a);
obj.a = "a";
console.log(obj.a);
delete obj.a;
console.log(obj.a);
// 输出
// undefined
// "a"
// undefined
delete真的可以删除对象属性吗?
先来考虑一个问题,当我们在用delete操作符的时候,我们究竟想要的是什么结果?答案必然是希望想把某属性从某个对象中去掉。可是delete操作符,真的能做到这一点吗?请看下列代码:
function CreateObj(){
this.a = "a";
}
CreateObj.prototype.a = "a";
var obj = new CreateObj();
console.log(obj.a);
delete obj.a;
console.log(obj.a);
// 输出
// "a"
// "a"
从上述代码中我们可以看到,尽管我们用了delete来删除obj下的a。可是删除后,仍然可以顺着原型链找到a这个属性。这显然不是我们想要的结果,假如在我们的业务代码中用是否能访问到obj.a来作判断,那就极有可能会被delete坑了。
delete引发的“后遗症”
如果上面的例子,delete产生的副作用是显式的。但其实,delete还会引发一个隐式的副作用。就是会把我们的对象的访问变慢。
也许有的朋友第一次听说,delete会导致对象访问变慢。要解释这个问题,我们需要先从JavaScript的容器说起,也就是V8。
V8的快属性与慢属性
我们都知道在js中我们可以随便给任何一个对象定义一个key-value。这种数据结构就跟字典很相似,由于字典是非线性结构,又有着查找速度慢的特点。因此V8在设计时,实际上是有对js对象的结构作优化的,而是采用了快慢属性这种综合方案。
先来执行一段代码:
function Foo() {
this[3] = '3'
this[1] = '1'
this["B"] = 'B'
this[2] = '2'
this["A"] = 'A'
this["C"] = 'C'
}
var bar = new Foo()
for(key in bar){
console.log(`index:${key} value:${bar[key]}`)
}
// 输出
// index:1 value:1
// index:2 value:2
// index:3 value:3
// index:B value:B
// index:A value:A
// index:C value:C
我们可以看到对象的key查找,会优先把key为数字的内容打印,再打印key为字符串的内容。并且输出的数字key是自动排了序的,而字符串的key的顺序就是我们设置的顺序。为什么会这样呢?
是因为在V8中,在对象中的属性分为了排序属性和常规属性,而这两种属性都是用线性的数据结构存放的。key为数字的内容会放在排序属性下,key为字符串的内容会放在常规属性下。在默认情况下,js对象会有2个隐藏属性elements和properties。 element中的内容是排序属性,按索引线性的存放。而properties下的放的是常规属性,用字典的方式存放的。可是为了提高常规属性的查找,V8又会把一定数量的常规属性(一般是10个),直接放在对象内部。借用李兵老师的图:
其中element和内属性这种线性结构的属性,我们称为快属性。而properties下的属性,称为慢属性。
delete的影响
在了解了对象的快属性和慢属性之后,现在我们回过头来讨论,为什么delete会有“后遗症”。我们想想,当用delete的时候,如果要删除的属性在线性属性当中,delete是不会直接把那块内存释放的,就会导致线性结构被破坏。之后如果再执行查找的时候,性能就会被影响。
那么应该怎么删除对象属性?
既然delete无法释放内存,为了保证js对象中线性结构的完整。我们其实可是直接用obj.xxx = undefined 这种方式来删除对象。因为这样打印出来的结果更delete是一致的,并且可以阻止原型链的后续查找,影响最小。
总结
今天我们了解了js对象中的快慢属性,以及delete操作符删除对象属性的2个问题。一是删除之后的属性,仍然可以通过原型链访问,二是有可能会破坏对象中的线性结构,导致对象访问性能下降。然后我们给出了一个删除对象属性的替代方案,就是直接把该属性赋值为undefined。希望对大家有所帮助。
参考
developer.mozilla.org/zh-CN/docs/…
《图解Google V8》——李兵