为什么delete会保留Array元素?

时间:2012-04-02 10:08:19

标签: javascript operators

今天我偶然发现了Stack Overflow上的一个问题 - How do I remove objects from a javascript associative array?。令我印象深刻的是,接受的答案既误导又大幅上调,所以我强调了可能的陷阱。

然而,在纠正错误答案的同时,我意识到我不知道为什么delete保持元素 assign {{ 1}}而不是删除。

undefined

背后有理由吗?

4 个答案:

答案 0 :(得分:11)

  

我意识到我不知道为什么delete分配未定义而不是删除是有意义的。

没有。 delete从对象中删除属性,将其设置为undefined。这是一个简单的方法:

var a = ['a', 'b', 'c'];
console.log(1 in a); // logs "true"
delete a[1];
console.log(1 in a); // logs "false"

请注意,在delete之后,a不再具有名为1的属性。完全没有。

对比:

var a = ['a', 'b', 'c'];
console.log(1 in a); // logs "true"
a[1] = undefined;
console.log(1 in a); // logs "true"

在那里,a仍有一个名为1的属性,只是该属性的值为undefined.

在JavaScript中了解arrays aren't really arrays at all非常有用。它们只是对象,数组“索引”只是属性名称(它们是字符串 - 是的,实际上,我们只是将它们写成数字),数组具有特殊的属性名称处理,这些属性都是数字(索引),特殊的length属性,以及从Array.prototype获得的一些函数。这在规范的Section 15.4中非常明确。一旦你坚定地认为JavaScript数组不是真正的数组,它们就更有意义了。 : - )

从数组中删除数组“index”属性不会更改其length(即使删除编号最大的数据也不会更改);它只是在数组中创建了一个洞(JavaScript“数组”本质上是稀疏数组;例如,它们可能有间隙)。所以在我上面的第一个例子中,如果我这样做的话,我会得到完全相同的数组:

var a = [];
a[0] = 'a';
a[2] = 'c';

注意间隙,数组没有1元素/属性。

如果你说:

var foo = a[3];

... foo可以获得值undefined,原因完全不同:

  1. a有一个名为3的属性,其值为undefined,或者:
  2. a根本没有名为3的属性;对于没有该名称属性的对象的属性访问器操作的结果是undefined。这个if-no-property-return - undefined以相当复杂的方式涵盖在规范中,但主要在Section 8.12.3中。
  3. 这些是截然不同的事情。

答案 1 :(得分:6)

它不会指定undefined。它删除了该属性。 (如果您尝试访问不存在的属性,您将获得undefinedlength基于数组中编号最大的项目。)

这是有道理的,因为它可以在任何类型的对象上运行。为了使其起作用,如果它们是instanceof Array,则必须使用特殊情况对象,但前提是它是具有数字名称的属性。

如果要从数组中删除项目,请使用splice

> var elements = [NaN, NaN, NaN];
> elements.splice(1,1);
> console.log(elements);
[ NaN, NaN ]

答案 2 :(得分:3)

JavaScript数组可以“稀疏”。也就是说,在从未分配值或删除值的意义上,某些槽可以是空的。如果您测试与该索引关联的值,则会返回undefined,因为它不存在,而不是因为它被赋值undefined

delete从数组中删除项目时,它不会自动向上滑动其余元素以填充空间:其他元素保留其现有索引,这反过来意味着.length属性不会更改,因为.length被定义为等于最高分配的索引加一。

如果要删除数组元素并重新编号其他元素,请使用.splice() method

答案 3 :(得分:1)

这是因为delete运算符删除了属性,而删除的属性具有值undefined。要从数组中删除元素,可以使用splice数组方法。 所有这一切都是因为javascript中的delete运算符如何工作。

当你没有设置对象的属性并尝试检查它的值时,它将是未定义的:

var obj = {};
alert(obj.foo); // undefined

与:

相同
alert(obj['foo']); // undefined

看看这个:

// create empty object
var obj = {};
// check its property named 1
alert(obj[1]); // undefined

// set property named 1 to value 'fooo'
obj[1] = 'fooo';
// and check it
alert(obj[1]); // 'fooo'

// now delete it 
delete obj[1];
// and check again
alert(obj[1]); // undefined 

delete删除了属性,其值为undefined - 以上所有代码都是关于对象的。

现在看看数组:

var arr = []; // it's better to use [] than new Array();
alert(arr[1]); // undefined  - same as above

// assign value
arr[1] = 'fooo' 
// check it
alert(arr[1]); // 'fooo' - same as above

// remove it 
delete arr[0];
// and check
alert(arr[1]); // undefined - same as above

所以行为是一样的,但是数组的length属性呢?规范http://es5.github.com/#x15.4.5.2说:

  
    

“此Array对象的length属性是一个数据属性,其值始终在数字上大于名称为数组索引的每个可删除属性的名称。”

  

所以当你看到这个:

var arr = ['foo', 'bar', 'foobar'];
alert(arr.length); // 3
// delete first
delete arr[1]; 
// and check length
alert(arr.length); // 3

最后一次检查给出3,因为此数组中的最后一个deletable属性具有索引2 - 第一个属性(索引为0)具有值undefined(删除运算符设置此值),第二个项目(具有索引1)具有值'bar'和第三个(索引2)的值为'foobar'。所以根据规格长度= 2 + 1('总体上数字大于最后删除');

这在以下代码中也可见:

var arr = [];

arr[10] = 'foo';
// element with index 10 is set to 'foo' but elements from 0 to 9 don't have value - they are undefined
// now check length
alert(arr.lenght); // 11 

最后一个可删除属性索引是10,所以10 + 1给出11,尽管以前的元素是未定义的。

因此删除操作符可以完成其工作,但它不是为了从数组中删除项目而设计的。