假设我创建一个对象如下:
var myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI",
"regex": "^http://.*"
};
删除该属性的最佳方法是什么 regex
结束新的 myObject
如下?
var myObject = {
"ircEvent": "PRIVMSG",
"method": "newURI"
};
假设我创建一个对象如下:
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
?对象可以被视为一组键值对。我称之为'值'的是原始的或对其他对象的引用,与该'键'相关联。
使用 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
但是,多态数组存在两个主要的无法解决的问题:
人们可能将多态性比作药物成瘾。乍一看,它似乎非常有利可图:漂亮而又蓬松的代码。然后,编码器将他们的阵列引入多态性药物。即时地,多态数组变得效率较低,并且它永远不会像以前那样有效,因为它是药物。为了将这种情况与现实生活联系起来,可卡因的某些人甚至可能无法操作简单的门把手,更不用说能够计算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时,它不能被提升回整数。""
作为空值。null
。但是,要小心!现在不要突然开始使用所有预先存在的代码执行此操作,因为它可能会破坏此类预先存在的代码和/或引入奇怪的错误。相反,这种有效的实践需要从一开始就实现,并且当转换预先存在的代码时,建议您对所有与之相关的行进行双倍,三倍,四倍检查,因为尝试将旧代码升级到此新实践可以是有风险,因为它是有益的。
var myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
delete myObject.regex;
console.log ( myObject.regex); // logs: undefined
这适用于Firefox和Internet Explorer,我认为它适用于所有其他人。
更新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
并且浪费了空间,如果阵列需要精确,这可能会有问题。
这是一个摆脱困境的可怕理由 null
S--null
如果使用不当会危险,而且与“精确度”无关。你不应该这样做的真正原因 delete
从一个数组是因为留下垃圾填充和混乱的数据结构是草率和容易出错。
接下来是一个设计得很啰嗦的场景,所以你可以跳到这个部分, 解决方案, 如果你想。我离开这一部分的唯一原因是因为我认为有些人可能认为这很有趣,而且我不想成为那个发布“有趣”答案然后从中删除所有“有趣”的“那个人”。 。
......我知道,这很愚蠢。
例如,假设您正在创建一个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先生。万岁! (我还是告诉他,不过......)
我觉得指出这两个同名的函数之间的区别非常重要,因为它们都非常有用。
.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]
.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。每个人都越早撤回对那些旧/深奥浏览器的支持,浏览器供应商越早实际遵循网络标准并采用新技术,我们越早越好一个更安全的网络。
老问题,现代答案。使用对象解构,一个 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);
编辑:
要重新分配到同一个变量,请使用 let
:
let myObject = {"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
({ regex, ...myObject } = myObject);
console.log(myObject);
另一种选择是使用 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()
可以以类似的方式使用。
您在问题标题中使用的术语 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.seal
: Object.seal()
假设您有一个如下所示的对象:
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
也会搞砸你的索引。
因此,在从对象中删除值时,始终首先考虑您是在处理对象属性还是处理数组值,并根据它选择适当的策略。
如果你想试验这个,你可以使用 这个小提琴 作为一个起点。
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
该 删除运算符 这是最好的方法。
一个实例来展示:
var foo = {bar: 'bar'};
delete foo.bar;
console.log('bar' in foo); // Logs false, because bar was deleted from foo.