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

7445

给定一个对象:

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

如何删除属性regex以得到以下的myObject

let myObject = {
  "ircEvent": "PRIVMSG",
  "method": "newURI"
};
38个回答

9801

要从一个对象中删除属性(更改该对象),可以这样做:

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

Demo

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

console.log(myObject);

对于任何对此感兴趣的人,Stack Overflow用户 kangax在他们的博客 Understanding delete上写了一篇非常深入的关于delete语句的博客文章。强烈推荐。

如果您想要一个新的对象,其中包含除某些键之外所有原始键,您可以使用解构

Demo

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

// assign the key regex to the variable _ indicating it will be unused
const {regex: _, ...newObj} = myObject;

console.log(newObj);   // has no 'regex' key
console.log(myObject); // remains unchanged


22
这是一个不错的方法,但只有在你实际使用“正则表达式”时才有效,否则eslint会抱怨未使用的变量。 - Loolooii
3
@Loolooii,你可以在解构赋值中重命名变量,以满足no-unused-vars规则中的argsIgnorePattern。这是一个容易解决的问题。 - PartyLich
4
使用这种方法的问题在于,如果解构赋值在条件语句内部,会导致 ESlint 报错。 - ankush981
3
很遗憾好内容消失了,但很高兴我们有“回溯机”:https://web.archive.org/web/20210224201033/http://perfectionkills.com/understanding-delete/ :) - Lea de Groot
2
理解删除文章:我发现URL http://perfectionkills.com/understanding-delete有效(不带最后的斜杠) - kca
显示剩余7条评论

1116
JavaScript中的对象可以被看作是键和值之间的映射。 delete 操作符用于逐个删除这些键,通常称为对象属性。


```js let obj = { key1: "value1", key2: "value2" }
delete obj.key1; // removes the key1 property from obj ```
var obj = {
  myProperty: 1    
}
console.log(obj.hasOwnProperty('myProperty')) // true
delete obj.myProperty
console.log(obj.hasOwnProperty('myProperty')) // false

delete 运算符并不会直接释放内存,它与仅将属性的值赋为 nullundefined 不同,因为它会从对象中删除属性本身。请注意,如果被删除属性的值是引用类型(即对象),并且程序的其他部分仍然保留着对该对象的引用,则该对象在所有对它的引用消失之前,当然不会被垃圾回收。

delete 仅适用于描述符标记为可配置的属性。


404

老问题,现代答案。使用对象解构,一种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 try-out 编辑器中查看它的实际操作。


编辑:

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

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

3
如果属性名是可变的,例如我将其存储在一个变量中,如何进行解构赋值? - Ceres
1
请查看下面的答案:https://dev59.com/5nVC5IYBdhLWcg3wsTfv#52301527 - Koen.
4
为什么这比 delete() 更可取?“现代化”并不是一个理由... - GreenAsJade
1
我并不是在说它是这样的,我只是提供了一种替代方案。虽然delete以前有一些性能问题,但我认为这些问题已经在本页面的其他答案中得到了描述。 - Koen.
19
你偏爱这样做的一个重要原因是它不会改变原始对象。对于像React这样的框架非常重要。实际上,我在寻找一个不会改变原始对象的答案。 - Silidrone
1
我能否使用对象解构将要排除的props放入props数组中,而不是用逗号分隔的列表? - Timo

318

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

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

这在Firefox和Internet Explorer中有效,我认为在其他所有浏览器中也有效。

298
< p > delete 运算符用于从对象中删除属性。

const obj = { foo: "bar" };

delete obj.foo;
obj.hasOwnProperty("foo"); // false

请注意,对于数组来说,这与删除元素不是一回事。要从数组中删除一个元素,请使用Array#spliceArray#pop。例如:

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

详情

严格来说,JavaScript中不可能真正删除任何内容。 delete 操作符既不会删除对象也不会释放内存。相反,它将其操作数设置为 undefined 并操纵父对象以使成员消失。

let parent = {
    member: { str: "Hello" }
};
let secondref = parent.member;

delete parent.member;
parent.member;        // undefined
secondref;            // { str: "Hello" }
< p >对象并没有被删除,只是其引用被删除。当所有引用都被移除时,内存才会被垃圾回收器释放。< /p >

另一个重要的注意点是,delete 操作符不会为您重新组织结构,这可能会导致看起来不直观的结果。例如,删除数组索引将在其中留下“空洞”。< /p >

let array = [0, 1, 2, 3]; // [0, 1, 2, 3]
delete array[2];          // [0, 1, empty, 3]

这是因为数组对象。所以索引和键是相同的。

let fauxarray = {0: 1, 1: 2, length: 2};
fauxarray.__proto__ = [].__proto__;
fauxarray.push(3);
fauxarray;                // [1, 2, 3]
Array.isArray(fauxarray); // false
Array.isArray([1, 2, 3]); // true

在JavaScript中,不同的内置函数对待带有空洞的数组的方式是不同的。

  • for..in 语句会完全跳过空索引。

  • 一个朴素的 for 循环将返回该索引处的值为 undefined

  • 使用 Symbol.iterator 的任何方法都将返回该索引处的值为 undefined

  • forEachmapreduce 简单地跳过缺失的索引,但不会移除它。

例如:

let array = [1, 2, 3]; // [1,2,3]
delete array[1];       // [1, empty, 3]
array.map(x => 0);     // [0, empty, 0]

因此,在常见的从数组中删除元素的用例中,不应使用delete运算符。数组有专门的方法来删除元素并重新分配内存:Array#splice()Array#pop

Array#splice(start [, deleteCount [, item1 [, item2 [,...]]] ])

Array#splice会改变数组,并返回任何已删除的索引。deleteCount个元素从索引start处移除,并将item1,item2 ... itemN从索引start插入到数组中。如果省略deleteCount,则从startIndex开始的元素将被删除到数组的末尾。

let a = [0,1,2,3,4]
a.splice(2,2) // returns the removed elements [2,3]
// ...and `a` is now [0,1,4]

此外,Array.prototype上还有一个名称类似但不同的函数:Array#slice

Array#slice([begin[, end]])

Array#slice是非破坏性的,并返回一个新数组,其中包含从startend指定索引的元素。如果未指定end,则默认为数组的结尾。如果end是正数,则指定从零开始的非包含索引停止的位置。如果end是负数,则通过从数组末尾向后计算所得的值指定要停止的索引(例如,-1会省略最后一个索引)。如果end <= start,则结果为空数组。

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

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

Array#pop

Array#pop会从数组中删除最后一个元素并返回该元素。这个操作会改变数组的长度。相反的操作是push

Array#shift

Array#shiftpop类似,不同之处在于它删除的是第一个元素。相反的操作是unshift


268

扩展语法(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 快两倍以上,但是该属性不会被删除并且可以进行迭代。

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

展开语法

这个ES6操作符允许我们返回一个全新的对象,不包括任何属性,而且不会改变原有的对象。但是它的性能比以上两种方式都要差,因此当您需要一次删除多个属性时,建议不要使用它。

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

5
这并不是在移除属性,而是创建一个浅拷贝,并且不复制指定的键和值。这是一个非常大的区别。 - RobG
这是一个大多数情况下很好的答案,但最后一个例子是荒谬的。这就像有人问如何处理桌子上的空可乐瓶,我的回答是订购一个全新的桌子,然后将旧桌子上的每一件物品都移动到新桌子上,除了空的可乐瓶。你正在创建一个全新的东西集,只是为了摆脱其中的一个子集。 - jdmneon

126
另一种选择是使用Underscore.js库。
请注意,_.pick()_.omit()都返回对象的副本,不会直接修改原始对象。将结果分配给原始对象应该就可以了(未显示)。
参考:链接 _.pick(object, *keys) 返回过滤后只有白名单键(或有效键数组)的值的对象副本。
var myJSONObject = 
{"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};

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

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

返回一个对象的副本,过滤掉被列入黑名单的键(或键数组)。

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

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

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


_.omit(obj, 'key') is cool but I needed to omit a nested key so I used Object.assign(obj, { filters: { sort_by: '' }}) - agm1984

123

克隆一个没有某个属性的对象:

例如:

let object = { a: 1, b: 2, c: 3 };

我们需要删除a

  1. With an explicit prop key:

    const { a, ...rest } = object;
    object = rest;
    
  2. With a variable prop key:

    const propKey = 'a';
    const { [propKey]: propValue, ...rest } = object;
    object = rest;
    
  3. A cool arrow function :

    const removeProperty = (propKey, { [propKey]: propValue, ...rest }) => rest;
    
    object = removeProperty('a', object);
    
  4. For multiple properties

    const removeProperties = (object, ...keys) => (keys.length ? removeProperties(removeProperty(keys.pop(), object), ...keys) : object);
    

使用方法

object = removeProperties(object, 'a', 'b') // result => { c: 3 }
或者
const propsToRemove = ['a', 'b']
object = removeProperties(object, ...propsToRemove) // result => { c: 3 }

1
为了避免以下警告:Parameter 'propKey' implicitly has an 'any' type. ts(7006),请为 propKey 添加类型,如下所示:const removeProperty = (propKey: string | number, { [propKey]: propValue, ...rest }) => rest - A-S

80

您在问题标题中使用的术语“从JavaScript对象中删除属性”可以有不同的解释。一种是将其从整个内存和对象键列表中删除,另一种是仅从对象中删除它。正如其他答案中所提到的那样,关键字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"];

即使您从对象中删除它,该特定对象也不会从内存中删除,因为“regex”变量和“myOtherObject [“regex”]”仍然具有它们的值。那么我们怎样才能确定地从内存中删除对象呢?
答案是要删除代码中指向该对象的所有引用,并且还不使用var语句创建指向该对象的新引用。关于var语句的最后一点是我们通常面临的最关键问题之一,因为使用var语句会防止创建的对象被删除。
这意味着在这种情况下,您将无法删除该对象,因为您已经通过var语句创建了“regex”变量,如果您这样做:
delete regex; //False

结果将会是false,这意味着你的删除语句未按预期执行。但如果你之前没有创建过该变量,且只有myOtherObject["regex"]作为你最后一个现有的引用,你可以通过以下方式直接删除它:
myOtherObject["regex"] = null;
delete myOtherObject["regex"];

换句话说,只要代码中没有指向该对象的引用,JavaScript对象就可以被释放。

更新:

感谢@AgentME:

在删除属性之前将其设置为null没有任何作用(除非对象已被Object.seal密封并且删除失败。这通常不是情况,除非您特别尝试)。

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


一个 JavaScript 对象在你的代码中没有指向该对象的引用时,就会被立即销毁。这句话不应该说“有资格被销毁”,因为垃圾回收器可能会拖延销毁这个对象。 - Ted Brownlow

56

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 operator)在尝试删除其任何属性时返回 false。如果属性是可配置的,则返回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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接