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

Flu*_*ffy 40 javascript

使用如下代码是不好的:

var a = [1,2,3,4];
a.length = 2; // 3 and 4 are removed
Run Code Online (Sandbox Code Playgroud)

它有不错的浏览器支持吗?删除的值是否正确收集垃圾?

Pau*_*per 31

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

是.这是自ECMAScript的第一版以来一直存在的:

具体来说,每当添加名称为数组索引的属性时,如果需要,将更改length属性,使其大于该数组索引的数值; 每当更改length属性时,将自动删除名称为数值索引且值不小于新长度的每个属性.

标准ECMA-262,15.4

在ECMAScript 5中,这已移至15.4.5.2.

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

标准ECMA-262,15.4.5.2

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


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

是.(见上文引用,和15.4.5.1.)


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

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

确实,具有非微妙效果的属性设置器在JS中有点不寻常,因为它们的副作用可能是不明显的.但是自从.length第0天开始使用Javascript 以来,它应该被广泛理解.

如果您想要替代方案,请使用.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
Run Code Online (Sandbox Code Playgroud)

如果.length由于某种原因我避免设置,那将是因为它改变了数组,我更喜欢不可变的编程.不可改变的替代方案是.slice().

a.slice(0, n);  // returns a new array with the first n elements
Run Code Online (Sandbox Code Playgroud)

  • `splice()` 函数的 `deleteCount` 参数是可选的。如果省略,则索引 n 中的所有元素都将被删除。因此,简单地调用“a.splice(n)”(不带第二个参数)将具有与“a.length = n”相同的效果 (2认同)

Tra*_*s J 29

数组是异国情调的对象.

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

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

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

还概述了此阵列奇异对象的特定属性键的行为.

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

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

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

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

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

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

[...]并且每当更改length属性的值时,将删除其名称为值不小于新长度的数组索引的每个属性.

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

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

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

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

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

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

1. 对象类型 ECMA 6
2. 阵列外来对象 ECMA 6
3. ArraySetLength(A,Desc) ECMA 6

  • 1+很好的答案.希望现在得到一些观点,问题出在"热门网络问题"列表中. (2认同)

Hen*_*dry 12

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

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

甚至还有一个如何在Mozilla Developer Network上使用它来缩短数组的示例

if (statesUS.length > 50) {
    statesUS.length = 50;
}
Run Code Online (Sandbox Code Playgroud)

  • @Seiyria:如果有可能处理美国少于50个州的州名单(例如,如果处理美国少于50个州的历史数据,或只是处理某些州的子集).在这种情况下,如果你只是在没有检查的情况下将长度设置为50,那么最后你会得到空的垃圾条目. (8认同)
  • 那个例子很傻; `if`检查完全没必要! (3认同)

Mig*_*ota 9

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

var truncated = a.slice(0,2)
Run Code Online (Sandbox Code Playgroud)

Slice确实创建了一个新阵列.

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

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

  • 我想你是在建议使用模式`a = a.slice(0,20)`?但是`Array.prototype.slice`是用于复制的,并且它可以用来实现元素的删除并不是很明显.只是我自己的意见. (2认同)