在JavaScript中设置数组长度是反模式吗?

时间:2015-07-21 18:52:17

标签: javascript

使用以下代码是不好的:

var a = [1,2,3,4];
a.length = 2; // 3 and 4 are removed

它是否有适当的浏览器支持?删除的值是否可以正确收集垃圾?

7 个答案:

答案 0 :(得分:29)

  

它有不错的浏览器支持吗?

是。自very first edition of ECMAScript

以来一直存在
  

具体来说,每当添加名称为数组索引的属性时,   如果需要,将length属性更改为比该数组索引的数值多一个;什么时候   length属性被更改,每个属性的名称都是一个数组索引,其值不小于   新长度会自动删除。

     

标准ECMA-262,15.4

在ECMAScript 5中,这已移至15.4.5.2

  

尝试将Array对象的length属性设置为一个值,该值在数值上小于或等于数组的现有数组索引不可删除属性的最大数字属性名称,将导致长度设置为a数值,该值大于该最大数字属性名称。

     

标准ECMA-262,15.4.5.2

所有浏览器都支持此行为。

  

删除的值是否可以正确收集垃圾?

是。 (请参阅上面的引文和15.4.5.1。)

  

在Javascript中设置数组长度是反模式吗?

不,不是真的。阵列是"异国情调的对象"在ES6中,关于数组的真正疯狂的独特之处在于它们的索引,而不是设置length属性。您可以使用属性setter复制相同的行为。

确实,具有非微妙效果的属性设置器在JS中有点不寻常,因为它们的副作用可能不明显。但是,由于{0}从第0天开始在Javascript中出现,所以它应该被广泛理解。

如果您想要替代方案,请使用.length

.splice()

如果由于某种原因我避免设置// a is the array and n is the eventual length a.length = n; a.splice(n, a.length - n); // equivalent a.splice(n, a.length); // also equivalent ,那将是因为它改变了数组,我更喜欢不可变的编程。不可变的替代方案是.length

.slice()

答案 1 :(得分:28)

数组是异国情调的对象

  

异域对象是任何形式的对象,其属性语义与默认语义有任何不同。 1

数组的属性语义是特殊的,在这种情况下,更改长度会影响数组的实际内容。

  

Array对象是一个奇特的对象,它对数组索引属性键进行特殊处理[...] 2

此外,还概述了此数组奇异对象的特定属性键的行为。

  

[...]每个Array对象都有一个length属性,其值始终是小于2 ^ 32的非负整数。 length属性的值在数值上大于名称为数组索引的每个属性的名称;无论何时创建或更改Array对象的自有属性,都会根据需要调整其他属性以保持此不变[...]

本节详细说明length属性是32位整数。它还说如果添加数组索引,则改变长度。这意味着当使用不是索引的自己的属性名称时,长度不会改变,并且非索引的名称在数值上被认为小于索引。这意味着如果你有一个数组并且还添加了一个字符串(不是隐式数字,如不是" 3")属性,那么改变删除元素的长度将不会删除与字符串属性。例如,

var a = [1,2,3,4];
a.hello = "world";
a.length = 2; // 3 and 4 are removed
console.log(a.hello);//"world"
  

[...]具体来说,每当添加一个名为数组索引的属性时,如果需要,将更改length属性的值,使其大于该数组索引的数值; [...]

除截断外,还可以使用索引进行扩展。如果使用索引值(基本上是整数),则将更新长度以反映该更改。由于JavaScript中的数组是稀疏的(如同,没有允许间隙),这意味着为较大的索引添加值可以使数组更大。

var a = [1,2,3,4];
a[50] = "hello";
console.log(a.length);//51
  

[...]并且只要更改了length属性的值,就会删除名称为数值索引且其值不小于新长度的每个属性。

最后,这是OP提出的具体问题。当修改数组的length属性时,它将删除每个索引和值,这些索引和值在数值上大于新长度值减去1.在OP的示例中,我们可以清楚地看到将长度更改为2已删除索引2和3处的值,只留下索引为0和1的前两个值。

上面提到的删除算法可以在ArraySetLength(A, Desc)定义中找到。

  
      
  1. 湾让deleteSucceeded为A.[[Delete]](ToString(oldLen)) 3
  2.   

将索引转换为字符串并使用索引上对象的删除行为。此时整个属性被删除。由于这内部达到了删除调用,因此没有理由相信它会泄漏内存,甚至是反模式,因为它在语言规范中有明确描述。

总之,它是否有适当的浏览器支持?是的。 删除的值是否正确收集垃圾?是的。

然而,是一种反模式吗?这可以用任何一种方式论证。它真正分解的是对使用相同代码的其他人的语言的熟悉程度,这是说它可能没有最佳可读性的好方法。另一方面,由于JavaScript确实占用了每个字符所占用的带宽或内存(因此需要缩小和捆绑),因此采用这种方法来截断数组会很有用。通常情况下,对这种类型的细节的担忧将是微观优化,并且应该根据相关代码的整体设计在个案的基础上考虑使用这种特殊属性。

<子> 1。 The Object Type ECMA 6
<子> 2。 Array Exotic Objects ECMA 6
<子> 3。 ArraySetLength(A, Desc) ECMA 6

答案 2 :(得分:12)

因为它是可写的https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length(在这里看到)应该可以这样使用

  

您可以设置length属性以随时截断数组。通过更改其长度属性来扩展数组时,实际元素的数量不会增加;例如,如果在当前为2时将长度设置为3,则该数组仍然只包含2个元素。

在Mozilla开发者网络

上有一个如何缩短数组的例子
if (statesUS.length > 50) {
    statesUS.length = 50;
}

答案 3 :(得分:9)

最好使用切片,因为你的意图很明确。

var truncated = a.slice(0,2)

Slice确实会创建一个新数组。

如果这对您来说是一个问题,那么修改长度属性没有任何问题。

它实际上是最快的截断方式,并且在所有浏览器中都受支持。 jsperf

答案 4 :(得分:4)

  

在JavaScript中设置数组长度是反模式吗?

没有。可以将其设置为截断数组。

  

它有不错的浏览器支持吗?

是的,请在此处查看兼容性矩阵:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length

  

删除的值是否可以正确收集垃圾?

是的,他们应该。他们真的会得到它们 - 如果这是一个真正令人担忧的话,你需要对JavaScript代码进行分析。

答案 5 :(得分:2)

这种截断数组的方法在 JavaScript: Good Parts 中提到,因此它不是反模式。

答案 6 :(得分:1)

这不是反模式。它在所有主流浏览器中都受支持。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/length