协慌网

登录 贡献 社区

如何从 JavaScript 对象中删除属性?

假设我创建一个对象如下:

var myObject = {
    "ircEvent": "PRIVMSG",
    "method": "newURI",
    "regex": "^http://.*"
};

删除属性regex以最终使用新的myObject的最佳方法是什么,如下所示?

var myObject = {
    "ircEvent": "PRIVMSG",
    "method": "newURI"
};

答案

喜欢这个:

delete myObject.regex;
// or,
delete myObject['regex'];
// or,
var prop = "regex";
delete myObject[prop];

演示

var myObject = {
    "ircEvent": "PRIVMSG",
    "method": "newURI",
    "regex": "^http://.*"
};
delete myObject.regex;

console.log(myObject);

对于有兴趣阅读更多相关内容的人来说,Stack Overflow 用户kangax在他们的博客上发表了一篇关于delete声明的非常深入的博客文章, 了解删除 。强烈推荐。

操作员delete意外慢!

看看基准

删除是删除对象属性而没有任何剩余的唯一真正方法,但与其 “替代” 相比,它的工作速度要慢 100 倍 ,设置object[key] = undefined

这个替代方案不是这个问题的正确答案!但是,如果你小心使用它,你可以大大加快一些算法。如果您在循环中使用delete并且性能有问题,请阅读详细说明。

什么时候应该使用delete并将设置值设置为undefined

对象可以被视为一组键值对。我称之为 '值' 的是原始的或对其他对象的引用,与该'key' 相关联。

当您将结果对象传递给您无法控制的代码时(或者您不确定您的团队或您自己时) ,请使用delete

从 hashmap 中删除密钥

var obj = {
     field: 1     
 };
 delete obj.field;

当您关心性能时,请使用设置为undefined 。它可以大大提升您的代码。

密钥保留在 hashmap 中的位置 ,只有值被undefined替换。明白, for..in循环仍会迭代该键。

var obj = {
     field: 1     
 };
 obj.field = undefined;

使用此方法,并非所有确定属性存在的方法都将按预期工作。

但是,这段代码:

object.field === undefined

两种方法的行为都相同。

测试

总而言之,差异都是关于确定属性存在的方法,以及关于for..in循环的方法。

console.log('* -> "Takes prototype inheritance into consideration, that means it lookups all over prototype chain too."');

 console.log(obj.field === undefined, 'obj.field === undefined', 'You get "undefined" value when querying for "field" in object-hashmap. *');

 console.log(obj["field"] === undefined, 'obj["field"] === undefined', 'Just another way to query (equivalent). *');

 console.log(typeof obj.field === "undefined", 'typeof obj.field === "undefined"', 'Get the value attached to "field" key, and check it\'s type is "undefined". *');

 console.log("field" in obj, '"field" in obj', 'This statement returns true if "field" key exists in the hashmap. False otherwise. *');

 console.log(obj.hasOwnProperty("field"), 'obj.hasOwnProperty("field")', 'This statement returns true if \'field\' key exists in the hashmap. The ONLY way NOT to lookup for property in the prototype chain!');
 //Object.keys().indexOf() is an overkill that runs much slower :)

 var counter = 0,
     key;
 for (key in obj) {
     counter++;
 }
 console.assert(counter === 0, 'counter === 0', '"field" is not iterated using "for .. in" loop. *');

小心内存泄漏!

虽然使用obj[prop] = undefineddelete obj[prop]更快,但另一个重要的考虑因素是obj[prop] = undefined可能并不总是合适的。 delete obj[prop]obj删除prop并从内存中删除它,而obj[prop] = undefined只是将prop的值设置为undefined ,使prop仍然在内存中。因此,在创建和删除许多密钥的情况下,使用obj[prop] = undefined会强制进行昂贵的内存协调(导致页面冻结)并可能导致内存不足错误。检查以下代码。

"use strict";
var theNodeList=[], i, current, numberOfNodes=65536, body=document.body, nodeRecords=[];
for (i = 0; i !== numberOfNodes; i++) {
    nodeRecords[i] = [];
    current = theNodeList[i] = document.createElement("div");
    current.textContent = i;
    document.body.appendChild( current );
}
var lastTime = -1;
requestAnimationFrame(function recordUpdates(){
    var currentTime = Math.round( performance.now()*1000 )
    for (i = 0; i !== numberOfNodes; i++) {
        if (lastTime !== -1) {
            // the previously collected data is no longer in use
            /*************************************************/
            /****/ nodeRecords[i][lastTime] = undefined; /****/
            /*************************************************/
        }
        nodeRecords[i][currentTime] = theNodeList[i].outerHTML;
    }
    lastTime = currentTime;
    requestAnimationFrame( recordUpdates );
});

在上面的代码中,只需执行nodeRecords[i][lastTime] = undefined;因为每个动画帧都会导致大量的内存泄漏。每个帧,所有 65536 个 DOM 元素将占用另外 65536 个单独的插槽,但之前的 65536 个插槽只会被设置为未定义,这会使它们挂在内存中。来吧,尝试在控制台中运行上面的代码并亲自查看。强制出现内存不足错误后,尝试再次运行它,除非使用以下版本的代码使用delete运算符。

"use strict";
var theNodeList=[], i, current, numberOfNodes=65536, body=document.body, nodeRecords=[];
for (i = 0; i !== numberOfNodes; i++) {
    nodeRecords[i] = [];
    current = theNodeList[i] = document.createElement("div");
    current.textContent = i;
    document.body.appendChild( current );
}
var lastTime = -1;
requestAnimationFrame(function recordUpdates(){
    var currentTime = Math.round( performance.now()*1000 )
    for (i = 0; i !== numberOfNodes; i++) {
        if (lastTime !== -1) {
            // the previously collected data is no longer in use
            /********************************************/
            /****/ delete nodeRecords[i][lastTime]; /****/
            /********************************************/
        }
        nodeRecords[i][currentTime] = theNodeList[i].outerHTML;
    }
    lastTime = currentTime;
    requestAnimationFrame( recordUpdates );
});

如上面的代码片段所示, delete运算符有一些罕见的适当用例。但是,不要太担心这个问题。这只会成为长寿命对象的问题,这些对象会不断添加新密钥。在任何其他情况下(几乎在实际编程中都是这种情况),最合适的是使用obj[prop] = undefined 。本节的主要目的只是为了引起您的注意,以便在您的代码中这很可能成为一个问题,那么您可以更轻松地理解问题,从而不必浪费时间来解析您的代码来定位并了解这个问题。

不要总是设置为undefined

Javascript 的一个重要考虑因素是多态性。多态性是指分配相同变量 / 对象中的不同类型,如下所示。

var foo = "str";
foo = 100;          // variable foo is now labeled polymorphic by the browser
var bar = ["Some", "example"];
bar[2] = "text";    // bar is a monomorphic array here because all its entries have the
                    // same type: string primitive
bar[1] = undefined; // bar is now a polymorphic array

但是,多态数组有两个主要的无法解决的问题:

  1. 它们速度慢,内存效率低。当访问特定索引时,浏览器不必仅获取数组的全局类型,而是必须基于每个索引获取类型,从而每个索引都存储其类型的其他元数据。
  2. 一旦多态,总是多态的。当一个数组变成多态时,多态性无法在 Webkit 浏览器中撤消。因此,即使您将多态数组还原为非多态数组,它仍将被浏览器存储为多态数组。

人们可能将多态性比作药物成瘾。乍一看,它似乎非常有利可图:漂亮的蓬松代码。然后,编码器将他们的阵列引入多态性药物。即时地,多态数组变得效率较低,并且由于它是药物,它永远不会像以前那样有效。为了将这种情况与现实生活联系起来,可卡因的某些人甚至可能无法操作简单的门把手,更不用说能够计算 PI 的数字了。同样,多态性药物上的阵列也不能像单态阵列那样有效。

但是,药物旅行类比如何与delete操作相关?答案在上面的代码片段中包含了最后一行代码。因此,让它重新审视,这次是扭曲。

var bar = ["Some", "example"];
bar[2] = "text";    // bar is not a polymorphic array here because all its entries have the
                    // same type: string primitive
bar[1] = "";        // bar is still a monomorphic array
bar[1] = undefined; // bar is now a polymorphic array

观察。 bar[1] = ""不强制多态,而bar[1] = undefined 。因此,应该始终尽可能为对象使用相应的类型,以免意外地导致多态性。一个这样的人可以使用以下列表作为一般参考来使他们前进。但是,请不要明确使用以下想法。相反,使用任何适用于您的代码的方法。

  • 使用类型为布尔基元的数组 / 变量时,请使用falseundefined作为空值。虽然避免不必要的多态性是好的,但重写所有代码以明确禁止它可能实际上会导致性能下降。使用共同的判断!
  • 使用键入数字原语的数组 / 变量时,请使用0作为空值。请注意,在内部,有两种类型的数字:快速整数(包括 2147483647 到 - 2147483648)和慢浮点双精度(包括NaNInfinity在内的任何其他内容)。当一个整数降级为 double 时,它不能被提升回整数。
  • 使用键入字符串原语的数组 / 变量时,请使用""作为空值。
  • 使用符号时,请等待,为什么使用符号?!?!符号是表现糟糕的 juju。编程为使用符号的所有内容都可以重新编程为不使用符号,从而产生没有符号的更快代码。符号实际上只是超低效的元糖。
  • 使用其他任何东西时,请使用null

不过,请注意!现在不要突然开始使用所有预先存在的代码执行此操作,因为它可能会破坏此类预先存在的代码和 / 或引入奇怪的错误。相反,这种有效的实践需要从一开始就实现,并且在转换预先存在的代码时,建议您对所有与之相关的行进行双倍,三倍,四倍检查,因为尝试将旧代码升级到此新实践可以是有风险,因为它是有益的。

var myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
    
delete myObject.regex;

console.log ( myObject.regex); // logs: undefined

这适用于 Firefox 和 Internet Explorer,我认为它适用于所有其他人。