如何从 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异常缓慢!

基准

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

此替代方法不是此问题的正确答案!但是,如果谨慎使用它,则可以大大加快某些算法的速度。如果您正在使用delete in 循环,并且在性能方面遇到问题,请阅读详细说明。

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

一个对象可以看作是一组键值对。我所说的 “值” 是连接到该 “键” 的原始对象或对其他对象的引用。

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

从哈希图中删除密钥

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

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

键保留在哈希图中的位置 ,仅将值替换为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 个插槽将仅设置为 undefined,这会使它们挂在内存中。继续,尝试在控制台中运行以上代码并亲自查看。强制执行内存不足错误后,请尝试再次运行它,但以下版本的代码使用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之外的任何数字)。将整数降级为双精度数时,不能将其提升回整数。
  • 当使用类型为字符串基元的数组 / 变量时,请使用""作为空值。
  • 使用符号时,请等待,为什么要使用符号?!?!符号对性能不利。可以将所有编程为使用符号的程序重新编程为不使用符号,从而可以使没有符号的代码更快。符号实际上只是超级无效的糖。
  • 使用其他任何东西时,请使用null

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

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

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

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

2018 年 7 月 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 语言,因此所有内容都是一个对象,包括arrays 。因此,我觉得有必要指出一个特别的警告。

在数组中,与普通的旧对象不同,使用deletenull的形式在垃圾后面留下垃圾,从而在数组中创建一个 “空洞”。

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 ,也就是煮到语言和不应该照顾关于它所使用的对象的类型 ,而ArrayObject的子类,具有专门用于处理数组的方法。因此,没有充分的理由delete来准备特殊情况以重新移动阵列,因为那样只会因不必要的工作而减慢速度。回想起来,我的期望是不现实的。

当然,这确实让我感到惊讶。因为我写这是为了证明我的运动反对 “空垃圾”:

忽略null固有的危险和问题以及浪费的空间,如果数组需要精确,则可能会出现问题。

这是摆脱了可怕的理由null S-- null只危险的,如果使用不当,它没有任何与 “精确”。您不应该从数组中delete的真正原因是留下乱七八糟的数据结构是草率且容易出错的。

接下来是一个人为 scenario 绕的人为场景,因此,如果需要,您可以跳至解决方案部分。我离开本节的唯一原因是,我认为某些人可能认为这很有趣,并且我不想成为发布 “搞笑” 答案然后随后删除所有 “搞笑” 内容的 “那个人” 。

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

人为和漫长的 PDP-11 方案

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

好吧,让我们只想说,你想在这个用户是谁运行 UNIX 1960 年运行的 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 用户使用。 las,他的计算机没有足够的内存来容纳最后一个整数(不要问他如何处理可变宽度数组...)

因此,他很生气地向您发送了一封电子邮件:

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.slice 与 Array.prototype.slice

我觉得有必要指出这两个相似命名的函数之间的区别,因为它们都很有用。

Array.prototype.splice(start,n)

.splice()数组,并返回删除的索引。从索引 index 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()是非破坏性的,并返回包含所指示的索引从一个新的数组startend 。如果未指定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()的行为.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;
}

事实证明,实际上存在一种内置方法来判断数组是否确实是数组,这就是 ECMAScript 5(2009 年 12 月)中引入的Array.isArray() )。我在寻找是否有一个问题来询问对象数组时发现了这一点,以查看是否有比我的解决方案更好的解决方案,或者是否有没有比我的解决方案更好的解决方案。因此,如果您使用的 JavaScript 版本早于 ECMA 5,则可以使用 polyfill。但是,我强烈建议不要使用is_array()函数,因为继续支持旧版本的 JavaScript 意味着继续支持实现旧版本 JavaScript 的浏览器,这意味着鼓励使用不安全的软件,并使用户面临遭受恶意软件威胁的危险。因此,请使用Array.isArray() 。使用letconst 。使用添加到该语言的新功能。 不要使用供应商前缀。从您的网站中删除该 IE polyfill 废话。删除 XHTML <!CDATA[[...废话,也废话了–我们在 2014 年改用 HTML5。] 每个人越早撤回对那些旧的 / 深奥的浏览器的支持,浏览器供应商实际上就越早遵循 Web 标准并接受该标准。新技术,因此我们可以尽快进入更安全的网络。

老问题,现代答案。使用对象分解( 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);

另一种选择是使用Underscore.js库。

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

参考: link _.pick(object,* keys)

返回对象的副本,将其过滤为仅具有列入白名单的键(或有效键的数组)的值。

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

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

参考: link _.omit(object,* keys)

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

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

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

对于数组,可以类似的方式使用_.filter()_.reject()

传播语法 (ES6)

对于需要它的人

要在该线程中完成 @Koen 答案,如果您要使用传播语法删除动态变量,可以这样进行:

const key = 'a';
        
const { [key]: foo, ...rest } = { a: 1, b: 2, c: 3 };

console.log(foo);  // 1
console.log(rest); // { b: 2, c: 3 }

* foo将是一个新变量,其值为a (为 1)。


扩展答案 😇
有几种常见的方法可以从对象中删除属性。
每个人都有自己的优缺点( 请检查此性能比较 ):

删除运算符
易读且简短,但是,如果您要对大量对象进行操作,则可能不是最佳选择,因为它的性能未得到优化。

delete obj[key];


重新分配
delete快 2 倍以上,但是不会删除该属性,并且可以对其进行迭代。

obj[key] = null;
obj[key] = false;
obj[key] = undefined;


点差算子
ES6运算符使我们可以返回一个全新的对象(不包括任何属性),而无需更改现有对象。不利的一面是,它的性能比上述更差,因此当您需要一次删除许多属性时,建议不要使用它。

{ [key]: val, ...rest } = obj;

您在问题标题中使用的术语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语句将阻止删除创建的对象。

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

delete regex; //False

结果将为false ,这意味着您的 delete 语句未按预期执行。但是,如果您以前没有创建过该变量,而仅使用myOtherObject["regex"]作为最后一个现有引用,则可以通过删除它来完成此操作,例如:

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

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


更新:感谢 @AgentME:

在删除属性之前将属性设置为 null 不会完成任何操作(除非该对象已由 Object.seal 密封并且删除失败。除非您特别尝试,否则通常情况并非如此)。

要获取有关Object.seal更多信息: Object.seal()

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

Reflect.deleteProperty(myJSONObject, 'regex');

等效于:

delete myJSONObject['regex'];

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

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

Object.freeze()使对象的所有属性均不可配置(除其他因素外)。当尝试删除其任何属性时, deleteProperty函数(以及delete 运算符 )将返回false 。如果 property 是可配置的,则即使 property 不存在,它也会返回true

deletedeleteProperty之间的区别在于使用严格模式时:

"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

假设您有一个看起来像这样的对象:

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;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也会使您的索引混乱。

因此,在从对象中删除值时,请始终首先考虑您是在处理对象属性还是在处理数组值,然后基于此选择适当的策略。

如果要尝试此操作,可以使用此 Fiddle作为起点。