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


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

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

删除该属性的最佳方法是什么 regex 结束新的 myObject 如下?

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

4910
2017-10-16 10:57


起源


在这里找到一个比较“删除”与“未定义”对比“空”的基准 jsben.ch/#/BWMiw - EscapeNetscape
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/... - titusfx


答案:


喜欢这个:

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 声明在他们的博客上, 了解删除。强烈推荐。


6839
2017-10-16 10:58



选中,它也适用于“删除myJSONObject ['regex'];”看到: developer.mozilla.org/en/Core_JavaScript_1.5_Reference/... - johnstok
上面“理解删除”链接中的一个观察结果的结果是,因为您不一定要删除变量,而只能删除对象属性,因此您无法“通过引用”删除对象属性 - var value = obj [ '支柱'];删除值//不起作用 - George Jempty
所以它实际上并没有删除它?它只是变得未定义,但关键仍然存在?我错过了什么吗? - Doug Molineux
@Pete不,它确实将其删除。鉴于: var x = {a : 'A', b : 'B'}; 比较: delete x.a; typeof x.a; /* "undefined" */ x.hasOwnProperty('a'); /* false */ 至 x.b = undefined; typeof x.b; /* "undefined" */; x.hasOwnProperty('b'); /* true */ - nickf
@ChristopherPfohl为我工作。就像我说的那样,它实际上非常深入,所以总结起来有点困难。上面答案中的基本响应对于几乎所有情况都是足够的,博客进入了更多边缘案例以及这些案例存在的原因。 - nickf


操作者 delete 出乎意料地慢了!

看着那(这 基准

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

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

什么时候应该使用 delete 当设定值为 undefined ?

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

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

从hashmap中删除密钥

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

使用设置 undefined 当你关心表现时。它可以大大提升您的代码。

key仍然在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] = undefined 比做更快 delete obj[prop],另一个重要的考虑因素是 obj[prop] = undefined 可能并不总是合适的。 delete obj[prop] 移除了 prop 从 obj 并从记忆中删除它 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 确实。因此,应始终尽可能为对象使用相应的类型,以免意外地导致多态性。一个这样的人可以使用以下列表作为一般参考来使他们前进。但是,请不要明确使用以下想法。相反,使用任何适用于您的代码的方法。

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

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


705
2018-02-12 17:48



属性被指定为undefined仍然是对象的属性,因此它不会被GC删除,除非误读了你的最后一段。 - Lance
我在这里触摸GC的主题是错误的。两种方法对GC都有相同的结果:它们删除链接到键的值。如果该值是对其他对象的最后一个引用,则该对象将被清除。 - Dan
属性被指定为undefined仍然是对象的属性,因此它不会被GC删除 GC不管理任何有关属性的内容。它收集和删除值。只要没有任何内容引用值(对象,字符串等),GC就会将其从内存中删除。 - meandre
顺便说一句,这是检查Javascript对象上是否存在属性的双重问题。运用 在 运营商是可靠但缓慢的。检查房产是否不是 未定义 “这不是正确的答案”,但它更快。 查 - rdllopes
这个答案是否仍然相关? jsperf目前正在下降,但是 这个基准 似乎表明速度差仅为25%,这是无处可去的 关 到了 “慢100倍” 在这个答案。 - Cerbrus


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

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

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


193
2017-10-16 11:03



有没有办法使用一个表达式从同一个数组中删除多个属性 - Aakriti.G
当尝试一次删除多个属性时,重新映射到新对象不是更好吗? - jasonscript


更新2018-07-21: 很长一段时间,我对这个答案感到尴尬,所以我觉得是时候把它搞定了一点。只需要一点评论,澄清和格式化,以帮助加快阅读这个答案的不必要的冗长和复杂的部分。


短版

这个问题的实际答案

正如其他人所说,你可以使用 delete

obj // {"foo": "bar"}
delete obj["foo"]
obj // {}
obj["foo"] // undefined

数组等价

delete 从一个数组。使用 Array.prototype.splice 代替。

arr // [1,2,3,4,5]
arr.splice(3,1); // 4
arr // [1,2,3,5]

长版

JavaScript是一种OOP语言,所以一切都是对象,包括 阵列。因此,我认为有必要指出一个特别的警告。

在数组中,与普通旧对象不同,使用 delete 留下垃圾的形式 null,在数组中创建一个“洞”。

var array = [1, 2, 3, 4];
delete array[2];
/* Expected result --> [1, 2, 4]
 * Actual result   --> [1, 2, null, 4]
 */

如你看到的, delete 并不总是按照人们的预期工作。该值被覆盖,但内存未重新分配。也就是说, array[4] 没有搬迁到 array[3]。这与之形成鲜明对比 Array.prototype.unshift,在数组的开头插入一个元素并将所有内容都移位(array[0] 变 array[1]等)

老实说,除了设置 null 而不是 undefined - 这是合法的奇怪 - 这种行为 不能 令人惊讶,因为 delete 是一个一元运算符,像 typeof,这是很难说的语言,不应该关心 类型 它正在被使用的对象,而 Array 是。的子类 Object 用方法 专为...设计 使用数组。所以没有充分的理由 delete有一个特殊的盒子用于重新移动阵列,因为这会减少不必要的工作。回想起来,我的期望是不切实际的。

当然,它 没有 让我感到惊讶因为我写这篇文章是为了证明我对“空垃圾”的讨伐:

忽视其中固有的危险和问题 null并且浪费了空间,如果阵列需要精确,这可能会有问题。

这是一个摆脱困境的可怕理由 nullS--null 如果使用不当会危险,而且与“精确度”无关。你不应该这样做的真正原因 delete 从一个数组是因为留下垃圾填充和混乱的数据结构是草率和容易出错。

接下来是一个设计得很啰嗦的场景,所以你可以跳到这个部分, 解决方案, 如果你想。我离开这一部分的唯一原因是因为我认为有些人可能认为这很有趣,而且我不想成为那个发布“有趣”答案然后从中删除所有“有趣”的“那个人”。 。

......我知道,这很愚蠢。

设计和冗长的PDP-11场景

例如,假设您正在创建一个Web应用程序,该Web应用程序使用JSON序列化来存储用于字符串中“tabs”的数组(在本例中, localStorage)。我们还要说代码在绘制到屏幕时使用数组成员的数字索引来“标题”它们。你为什么这样做而不仅仅是存储“标题”?因为... 原因

好的,我们只是说你正在努力为此节省内存  从1960年代运行UNIX运行PDP-11小型机的用户,并编写了自己的基于Elinks,符合JavaScript,易于打印机的浏览器,因为X11是 不可能的

除了使用以外越来越愚蠢的边缘情况 delete 在所述数组上将导致 null 污染数组,可能会导致应用程序中的错误。如果你检查 null,它会直接跳过数字,导致标签呈现为 [1] [2] [4] [5] ...

if (array[index] == null)
    continue;
else
    title = (index + 1).toString();
/* 0 -> "1"
 * 1 -> "2"
 * 2 -> (nothing)
 * 3 -> "4"
 */

是的,这绝对不是你想要的。

轮到你了 可以 保持第二个迭代器,就像 j,仅在从数组中读取有效值时递增。但这并不能完全解决问题 null 问题,你仍然要取悦它 拖钓 PDP-11用户。唉,他的电脑只是  有足够的内存来保存最后一个整数 (不要问他是如何设法处理可变宽度数组的......)

所以,他给你发了一封愤怒的电子邮件:

Hey, your webapp broke my browser! I checked my localStorage database after your stupid code made my browser segfault, and this is what I found:

>"tabs:['Hello World', 'foo bar baz', null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, ... ]"

After clearing my precious data, it segfaulted again, and I did a backtrace, and what do I find? WHAT DO I FIND!? YOU USE TOO MANY VARIABLES!

>var i = index;
>var j = 1;

Grr, I am angry now.
-Troll Davidson

现在,你已经到了最后。这家伙一直在抱怨你的应用程序,你想告诉他闭嘴,去买一台更好的电脑。

解决方案: Array.prototype.splice

幸运的是,数组  有一个专门的方法来删除索引和重新分配内存: Array.prototype.splice()。你可以写这样的东西:

Array.prototype.remove = function(index){
  this.splice(index,1);
}
...
array = [1, 2, 3, 4];
array.remove(2);
// Result -> [1, 2, 4]

就这样,你很高兴PDP-11先生。万岁! (我还是告诉他,不过......)

Array.prototype.splice vs Array.prototype.slice

我觉得指出这两个同名的函数之间的区别非常重要,因为它们都非常有用。

Array.prototype.splice(start,n)

.splice() 改变数组,并返回删除的索引。从索引开始切片数组, start,和 n 元素被切掉了。如果n未指定,则整个数组之后 start 被切掉(n = array.length - start)。

let a = [5,4,3,2,1];
let chunk = a.splice(2,2);

// a     [5,4,3,2,1]
// start  0 1 2 - -
// n      - - 1 2 -

chunk; // [3,2]
a;     // [5,4,1]

Array.prototype.slice(开始,结束)

.slice() 是非破坏性的,并返回一个包含指示索引的新数组 start 至 end。如果 end 没有指定,行为是相同的 .splice() (end = array.length)。由于某些原因,这种行为有点棘手 end 索引从1而不是0.我不知道为什么会这样做,但事实就是如此。另外,如果 end <= start,结果是一个空数组。

let a = [5,4,3,2,1];
let chunks = [
    a.slice(2,0),
    a.slice(2,2),
    a.slice(2,3),
    a.slice(2,5) ];

// a             [5,4,3,2,1]
// start          0 1 2 - -
// end, for...    - - - - -
//   chunks[0]  0 - - - - -   
//   chunks[1]    1 2 - - -
//   chunks[2]    1 2 3 - -
//   chunks[3]    1 2 3 4 5

chunks; // [ [], [], [3], [3,2,1] ]
a;      // [5,4,3,2,1]

这实际上不是正在发生的事情,但更容易想到这种方式。根据MDN,这是实际发生的事情:

// a             [5,4,3,2,1]
// start          0 1 2 - - -
// end, for...    - - - - - -
//   chunks[0]    0 - - - - -
//   chunks[1]    0 1 2 - - -
//   chunks[2]    0 1(2)3 - -
//   chunks[3]    0 1(2 3 4)5

指定的索引 end 只是从切片中排除。带括号的索引表示切片的内容。无论哪种方式,行为都不直观,并且它必然导致其公平分享一个一个错误,因此您可能会发现使包装器函数更接近地模拟其行为是有用的。 .splice()

function ez_slice(array, start = 0, n = null){
    if(!Array.isArray(array) || !is_number(start))
        return null;

    if(is_number(n))
        return array.slice(start, start + n);

    if(n === null)
        return array.slice(start);

    return null;
}

ez_slice([5,4,3,2,1], 2, 1) // [3]
ez_slice([5,4,3,2,1], 2)    // [3,2,1]

/* Fun fact: isNaN is unreliable.
 * [NaN, [], {}, 0, 1, Infinity, undefined, null, "Hi"].filter(isNaN)
 * [NaN, {}, undefined, "Hi"]
 *
 * What we want is...
 *
 * [NaN, [], {}, 0, 1, Infinity, undefined, null, "Hi"].filter(is_nan)
 * [NaN]
 */
function is_nan(num){
    return typeof num === "number"
        && num !== num;
}

function is_number(num){
    return !is_nan(num)
        && typeof num === "number"
        && isFinite(num);
}

请注意,包装函数的设计对类型非常严格,并将返回 null 如果有什么不对。这包括输入一个字符串 "3"。程序员需要努力学习他的类型。这是为了鼓励良好的编程实践。

有关的更新 is_array()

这是关于这个(现在删除)的片段:

function is_array(array){
    return array !== null
        && typeof array === "object"
        && typeof array.length !== "undefined"
        && array.__proto__ === Array.prototype;
}

事实证明,实际上有一种内置的方法来判断数组是否真的是一个数组,就是这样 Array.isArray(),在ECMAScript 5(2009年12月)中介绍。我发现这一点,同时想看看是否有一个问题要求从对象中告诉数组,看看是否有比我更好的解决方案,或者如果没有,则添加我的。因此,如果您使用的是早于ECMA 5的JavaScript版本,那么就是您的polyfill。但是,我强烈建议不要使用我的 is_array() 功能,因为继续支持旧版本的JavaScript意味着继续支持实现它们的旧浏览器,这意味着鼓励使用不安全的软件并使用户面临恶意软件的风险。所以,请使用 Array.isArray()。使用 let 和 const。使用添加到语言中的新功能。  使用供应商前缀。 删除 来自您网站的IE polyfill废话。删除那个XHTML <!CDATA[[... 废话 - 我们在2014年转移到HTML5。每个人都越早撤回对那些旧/深奥浏览器的支持,浏览器供应商越早实际遵循网络标准并采用新技术,我们越早越好一个更安全的网络。


134
2017-09-18 00:56



这个 delete 然而,关键字更方便,哈哈 - Braden Best
此方法不会修改可能仍在其他位置引用的原始对象。这可能是也可能不是问题,取决于它的使用方式,但要记住这一点。 - Tamas Czinege
@ B1KMusic这是从数组中删除元素的方法: 拼接 - wulftone
@wulftone nope,分割数组并没有删除任何值。我真的认为从需要删除特定值的数组中删除的最佳方法是使用 delete 并制作垃圾收集功能来清理它。 - Braden Best
我没有看到 splice 在你的编辑中,但是 remove 必定是 Array.prototype.remove = function(index) { this.splice(index, 1); }; - Ry-♦


老问题,现代答案。使用对象解构,一个 ECMAScript中6 功能,它很简单:

const { a, ...rest } = { a: 1, b: 2, c: 3 };

或者问题样本:

const myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
const { regex, ...newObject } = myObject;
console.log(newObject);

您可以在Babel试用编辑器中看到它的运行情况。


编辑:

要重新分配到同一个变量,请使用 let

let myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
({ regex, ...myObject } = myObject);
console.log(myObject);

92
2017-11-08 18:02



也许是因为目标是从对象中删除属性,而不是在没有属性的情况下创建新属性...虽然,您的解决方案是我的最爱,因为我更喜欢不可变的方式。 - Vingt_centimes
这个问题说“要结束 新 myObject的”。 - Koen.
根据 kangax.github.io/compat-table/esnext,对象休息属性没有针对ES6完成,没有浏览器实现,只有Babel提供支持。 - Justin C
对于没有不需要的道具的新对象分配,这实际上是一个甜蜜的单行,并且已经支持Node.js 8。* / 9. * with --harmony: node.green/... - Damaged Organic
这应该是2018年接受的答案:) - Yulian


另一种选择是使用 Underscore.js 图书馆。

注意 _.pick() 和 _.omit() 都返回对象的副本,不直接修改原始对象。将结果分配给原始对象应该可以解决问题(未显示)。

参考: 链接  _.pick(对象,*键)

返回对象的副本,过滤到只有值的值 列入白名单的密钥(或有效密钥数组)。

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

_.pick(myJSONObject, "ircEvent", "method");
=> {"ircEvent": "PRIVMSG", "method": "newURI"};

参考: 链接  _.omit(对象,*键)

返回对象的副本,过滤后省略 列入黑名单的键(或键组)。

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

_.omit(myJSONObject, "regex");
=> {"ircEvent": "PRIVMSG", "method": "newURI"};

对于阵列, _.filter() 和 _.reject() 可以以类似的方式使用。


67
2018-05-24 18:53



请记住,如果对象的键是数字,则可能需要 _.omit(collection, key.toString()) - Jordan Arseno
你为什么链接到维基百科,而不是 underscorejs.org? - E730
@ E730好问题。我不知道为什么其中一位编辑改变它指向wikipedia.org/wiki/Underscore.js。我把它改回了underscorejs.org。 - Thaddeus Albers
嗯...... Underscore慢了~100倍 delete obj[prop] 比...慢约100倍 obj[prop] = undefined。 - Jack Giffin


您在问题标题中使用的术语 Remove a property from a JavaScript object,可以用一些不同的方式来解释。一个是删除整个内存和对象键列表,另一个是将它从对象中删除。正如在其他一些答案中提到的那样 delete 关键字是主要部分。假设您的对象如下:

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

如果你这样做:

console.log(Object.keys(myJSONObject));

结果将是:

["ircEvent", "method", "regex"]

您可以从对象键中删除该特定键,例如:

delete myJSONObject["regex"];

然后你的对象键使用 Object.keys(myJSONObject) 将会:

["ircEvent", "method"]

但问题是,如果您关心内存并希望整个对象从内存中删除,建议您在删除密钥之前将其设置为null:

myJSONObject["regex"] = null;
delete myJSONObject["regex"];

这里另一个重点是要小心你对同一个对象的其他引用。例如,如果您创建一个变量,如:

var regex = myJSONObject["regex"];

或者将其添加为另一个对象的新指针,如:

var myOtherObject = {};
myOtherObject["regex"] = myJSONObject["regex"];

即使你从对象中删除它也是如此 myJSONObject,那个特定的对象不会从内存中删除,因为 regex 变量和 myOtherObject["regex"] 仍然有自己的价值观。那么我们怎么能从内存中删除对象呢?

答案是 删除代码中的所有引用,指向该对象 并且 不使用 var 用于创建对该对象的新引用的语句。最后一点是关于 var 陈述,是我们通常面临的最重要的问题之一,因为使用 var 语句将阻止创建的对象被删除。

这意味着在这种情况下,您将无法删除该对象,因为您已创建了该对象 regex 变量通过 var 声明,如果你这样做:

delete regex; //False

结果将是 false,这意味着您的删除语句尚未按预期执行。但是,如果您之前没有创建过该变量,那么您只有 myOtherObject["regex"] 作为你最后的现有参考,你可以通过删除它来完成这个:

myOtherObject["regex"] = null;
delete myOtherObject["regex"];

换句话说,只要代码中没有指向该对象的引用,JavaScript对象就会被终止。


更新: 感谢@AgentME:

在删除属性之前将属性设置为null无法完成   任何东西(除非对象已被Object.seal和   删除失败。除非你特别指出,通常情况并非如此   尝试)。

获取更多信息 Object.sealObject.seal()


34
2018-02-12 06:29



你错了 - 只有对象在JavaScript中通过引用传递,所以如果 myJSONObject.regex的值是一个字符串,您将其分配给其他对象,另一个对象具有此值的副本。 - Michał Perłakowski
你是对的,这是一个引用:“要小心你对同一个对象的其他引用。” - Mehran Hatami


假设您有一个如下所示的对象:

var Hogwarts = {
    staff : [
        'Argus Filch',
        'Filius Flitwick',
        'Gilderoy Lockhart',
        'Minerva McGonagall',
        'Poppy Pomfrey',
        ...
    ],
    students : [
        'Hannah Abbott',
        'Katie Bell',
        'Susan Bones',
        'Terry Boot',
        'Lavender Brown',
        ...
    ]
};

删除对象属性

如果你想使用整个 staff 数组,执行此操作的正确方法是执行此操作:

delete Hogwarts.staff;

或者,你也可以这样做:

delete Hogwarts['staff'];

同样,删除整个学生阵列将通过调用完成 delete Hogwarts.students; 要么 delete Hogwarts['students'];

删除数组索引

现在,如果要删除单个工作人员或学生,则该过程会有所不同,因为这两个属性本身都是数组。

如果您知道工作人员的索引,您可以这样做:

Hogwarts.staff.splice(3, 1);

如果你不知道索引,你还必须进行索引搜索:

Hogwarts.staff.splice(Hogwarts.staff.indexOf('Minerva McGonnagall') - 1, 1);

注意

虽然你在技术上可以使用 delete 对于数组,使用它会导致在调用时获得不正确的结果 Hogwarts.staff.length 稍后的。换一种说法, delete 会删除元素,但它不会更新值 length 属性。运用 delete 也会搞砸你的索引。

因此,在从对象中删除值时,始终首先考虑您是在处理对象属性还是处理数组值,并根据它选择适当的策略。

如果你想试验这个,你可以使用 这个小提琴 作为一个起点。


30
2018-02-21 18:09



我认为你应该经常使用 splice 在数组而不是 delete。 - Joel Trauger
@JoelTrauger:这就是我所说的;-) - John Slegers
是。我的评论是这样的 delete 甚至不应该是一件事。它的 splice OP正在寻找什么。 - Joel Trauger
@JoelTrauger:正如我试图解释的那样, delete 应该用于对象属性和 splice 对于数组元素。 - John Slegers
拼接真的很慢。虽然它应该用来代替 delete 在数组上,最好不要创建以它为中心的代码。 - Jack Giffin


ECMAScript 2015(或ES6)内置了 反映 目的。可以通过调用删除对象属性 Reflect.deleteProperty() 以目标对象和属性键作为参数的函数:

Reflect.deleteProperty(myJSONObject, 'regex');

这相当于:

delete myJSONObject['regex'];

但是如果对象的属性不可配置,则无法使用deleteProperty函数或删除运算符删除它:

let obj = Object.freeze({ prop: "value" });
let success = Reflect.deleteProperty(obj, "prop");
console.log(success); // false
console.log(obj.prop); // value

Object.freeze() 使对象的所有属性都不可配置(除了其他东西)。 deleteProperty 功能(以及 删除运算符)返回 false 当试图删除它的任何属性时。如果属性是可配置的,则返回 true,即使财产不存在。

和...之间的不同 delete 和 deleteProperty 是在使用严格模式时:

"use strict";

let obj = Object.freeze({ prop: "value" });
Reflect.deleteProperty(obj, "prop"); // false
delete obj["prop"];
// TypeError: property "prop" is non-configurable and can't be deleted

29
2018-01-10 16:36



@Gothdo它有更多的好处,特别是当你需要做一些功能性的东西时。例如。您可以将函数赋值给变量,作为参数传递或使用 apply, call, bind 功能... - madox2


删除运算符 这是最好的方法。

一个实例来展示:

var foo = {bar: 'bar'};
delete foo.bar;
console.log('bar' in foo); // Logs false, because bar was deleted from foo.

22
2018-04-28 05:14



值得注意的是,尽管使用了 delete 运营商是健康的 垃圾收集, 有可能 出乎意料地慢了,出于同样的原因,在很大程度上。 - John Weisz