如何在JavaScript中清空一个数组?

2193

有没有一种方法可以清空一个数组,如果可以的话可能使用 .remove()

例如,

A = [1,2,3,4];

我该如何清空它?


1
这里有一个带有不同可能性的基准测试:http://jsben.ch/#/7QyI1 - EscapeNetscape
17个回答

5575

清空一个已存在的数组 A 的方法:

方法1

(这是我对问题最初的回答)

A = [];

这段代码将把变量A设置为一个新的空数组。如果你没有在其他地方引用原始数组A,那么这是完美的,因为它实际上创建了一个全新的(空)数组。但是,如果你从另一个变量或属性引用了这个数组,那么原始数组将保持不变,所以你应该小心使用这种方法,只有当你只通过原始变量A引用该数组时才使用。
这也是最快的解决方法。
这段代码示例展示了使用这种方法可能遇到的问题:
var arr1 = ['a','b','c','d','e','f'];
var arr2 = arr1;  // Reference arr1 by another variable 
arr1 = [];
console.log(arr2); // Output ['a','b','c','d','e','f']

方法2(由Matthew Crumley建议,如建议

A.length = 0

这将通过将数组长度设置为0来清空现有数组。在ECMAScript 5的“严格模式”下,它也适用,因为数组的长度属性是一个可读/可写属性。 方法3(由安东尼建议,如建议
A.splice(0,A.length)

使用.splice()函数可以完美解决问题,但由于该函数会返回一个包含所有已删除项的数组,实际上它将返回原始数组的副本。基准测试表明,这对性能没有任何影响。 方法4(由tanguy_k建议,如suggested
while(A.length > 0) {
    A.pop();
}

这种解决方法不太简洁,而且与原始答案中引用的早期基准相反,是最慢的解决方法。

性能

在所有清除现有数组的方法中,方法2和方法3的性能非常相似,并且比方法4要快得多。请参见benchmark

Diadistis在下面的answer中指出的那样,用于确定上述四种方法性能的原始基准存在缺陷。原始基准重复使用已清除的数组,因此第二次迭代清除的是已经为空的数组。

以下基准修复了这个缺陷:http://jsben.ch/#/hyj65。它清楚地显示方法#2(长度属性)和方法#3(splice)是最快的(不计算不改变原始数组的方法#1)。


这是一个热门话题,引起了很多争议。实际上有许多正确的答案,因为这个答案已经被标记为接受的答案很长时间了,所以我会在这里列出所有的方法。


236
while (A.length) { A.pop(); }的翻译是:当A数组有元素时,不断从尾部弹出元素直到数组为空。不需要加上 > 0 的条件判断。 - Ivan Black
439
在我看来,“> 0”更易读。而且这两者之间没有性能差异。 - Philippe Leybaert
12
@daghan,你的意思并不是很清楚。即使在将一个新数组分配给a之后,b仍然持有对旧数组的引用。cd仍然引用同一个数组。因此,输出结果的差异是可以预料的。 - shovavnik
20
方法1不被计算在内,因为它没有清除数组,而是创建了一个新的数组。它不应该被包含在基准测试中。 - Philippe Leybaert
58
如果数组中的某个项是 falsey 值,则不能使用 while(A.pop())。举个例子,当 A = [2, 1, 0, -1, -2] 时,会使 A 变为 [2, 1]。即使使用 while(A.pop() !== undefined) 也不行,因为数组中可能有一个值为 undefined。这可能是编译器没有优化它的原因。 - jgawrych
显示剩余58条评论

2715
如果您需要保留原始数组,因为您还有其他引用它的地方需要更新,那么您可以通过将其长度设置为零来清除它,而无需创建新的数组:

如果您需要保留原始数组,因为您还有其他引用它的地方需要更新,那么您可以通过将其长度设置为零来清除它,而无需创建新的数组:

A.length = 0;

29
ECMAScript 5标准对此有什么规定? - Pacerier
251
在ES5中仍然有效。根据第15.4节的规定:“每当更改length属性时,所有名称为数组索引且值不小于新长度的属性都会自动删除。” - Matthew Crumley
14
即使在严格模式下,length仍然是一个特殊的属性,但它并非只读属性,因此它仍然可以工作。 - Matthew Crumley
16
我已经进行了一些测试,似乎将a.length设置为0并不是清空整个数组的最优方法。http://jsperf.com/length-equal-0-or-new-array。我认为如果您只有一个引用(并且您没有添加要保留的额外属性),那么创建新数组并将旧数组交给垃圾收集器处理可能更好,垃圾收集器会在适当时运行。 - Paweł Brewczynski
5
根据ECMAScript规范,需要删除新长度之上的元素,因此任何正确的实现都会使不可访问的元素可以被垃圾回收。这并不一定意味着它们会立即缩小实际数组的存储空间。具体来说,V8只有在新长度小于原始长度的一半时才重新分配数组,但将长度设置为零实质上会在幕后重新分配一个新数组�� - Matthew Crumley
显示剩余29条评论

320

这里是最快的实现方式,同时保留相同的数组(“可变”):

function clearArray(array) {
  while (array.length > 0) {
    array.pop();
  }
}

请注意,它不能简化为 while (array.pop()):测试会失败。
顺便提一下,MapSet 定义了 clear() 方法,因此对于 Array 也应该有 clear() 方法才更合理。
TypeScript 版本:
function clearArray<T>(array: T[]) {
  while (array.length > 0) {
    array.pop();
  }
}

相应的测试:

describe('clearArray()', () => {
  test('clear regular array', () => {
    const array = [1, 2, 3, 4, 5];
    clearArray(array);
    expect(array.length).toEqual(0);
    expect(array[0]).toEqual(undefined);
    expect(array[4]).toEqual(undefined);
  });

  test('clear array that contains undefined and null', () => {
    const array = [1, undefined, 3, null, 5];
    clearArray(array);
    expect(array.length).toEqual(0);
    expect(array[0]).toEqual(undefined);
    expect(array[4]).toEqual(undefined);
  });
});

这里是更新后的jsPerf:http://jsperf.com/array-destroy/32 http://jsperf.com/array-destroy/152

jsPerf离线了。类似的基准测试:https://jsben.ch/hyj65


5
你的回答是唯一又正确且快速的,但点赞数却比较少。看来人们更喜欢漂亮但慢的解决方案:/ - obenjiro
@naomik 但这是其中一项基本功能,本来应该默认就有的。 - thefourtheye
7
@thefourtheye 的解决方案很好,虽然我同意 @naomik 的观点,你不应该修改原生对象。说这个方法本来就存在不是重点,问题是你修改了全局变量,这是不好的。如果你提供代码供其他人使用,那么它就不应该有任何未预料到的副作用。假设另一个库修改了 Array.prototype,并且它正在做一些略微不同的事情,那么在你的整个代码中 [].clear() 就会稍微出错。这将不是一个愉快的调试过程。因此,总体信息是:不要修改全局变量。 - jpillora
3
不修改全局范围的整个要点是因为你不知道其他人的代码是否已经(或将会)使用该名称。我建议在本地范围内创建一个函数。因此,在您的应用程序/库的 IIFE 中,执行 function clear(arr) { while(arr.length) arr.pop(); },然后使用 clear(arr) 而不是 arr.clear() 来清除数组。 - jpillora
2
结果证明,这种方法比.splice().length=0慢得多。基准测试结果不正确。请查看我的更新答案。 - Philippe Leybaert
显示剩余7条评论

259
更跨浏览器友好且更优化的解决方案是使用以下代码使用splice方法清空数组A的内容: A.splice(0, A.length);

59
为什么这更加跨浏览器友好?哪些浏览器会出现 A.length 的问题? - stricjux
4
@jm2,你所说的并不完全正确。实际上,它修改了相关数组并影响了所有引用。请参见我在 jsFiddle 上的测试:http://jsfiddle.net/shamasis/dG4PH/ - Shamasis Bhattacharya
4
@alex 不是这样的,splice 修改了数组并返回被删除的元素。首先要阅读文档:https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/splice - David Hellsing
3
使用逗号运算符可以防止返回生成的数组:A.splice(0, A.length),0;。这将使返回值为0,就像A.length = 0;一样。仍会创建生成的数组并_可能_导致脚本运行变慢:([jsperf](http://jsperf.com/clear-an-array)慢了约56%)。浏览器实现会影响它,尽管我看不出为什么“splice”比设置“length”更快。 - Evan Kennedy
13
我发现只使用A.splice(0)也可以。 - corwin.amber
显示剩余7条评论

160

截至目前为止,没有少于2739个赞的答案都是误导性和不正确的。

问题是:“如何清空现有数组?”例如对于A = [1,2,3,4]

  1. 说“A = []是答案”是无知和完全不正确的。[] == []错误的

    这是因为这两个数组是两个单独的对象,有它们自己的两个身份,在数字世界中占据自己的空间,各自存在。


假设您的母亲要求您清空垃圾桶。

  • 你不会拿一个新的垃圾桶,就好像你已经完成了你被要求做的事情。
  • 相反,您会清空垃圾桶。
  • 您不会用一个新的空桶代替已满的桶,也不会像A = [1,2,3,4]; A = [];一样将填充桶上的标签“A”取下并粘贴到新桶中。

清空数组对象是最简单的事情:

A.length = 0;

这样,位于“A”下面的垃圾桶不仅是空的,而且干净得像新的一样!


  1. 此外,您无需在桶未清空之前手动清除垃圾!您只需要在一个回合内完全清空现有垃圾桶,而不是在桶未清空之前清理垃圾,例如:

while(A.length > 0) {
    A.pop();
}
  • 同时,将左手放在垃圾桶底部,用右手握住顶部以便拉出其中的内容,如下图所示:

  • A.splice(0, A.length);
    

    不,你被要求将它清空:

    A.length = 0;
    

    这是唯一正确地清空给定JavaScript数组内容的代码。


    19
    你提出的解决方案唯一的问题在于垃圾仍然存在,只是你改变了告示板,它说没有垃圾。对旧数组的引用仍应存在。当设置.length = 0时,你能确定垃圾回收器也将删除对数组及其属性的所有引用吗? 我认为它确实会这样做,但对我来说,这就像魔术一样。一个 .clear() 方法是可取的,以避免产生混淆至少要这么说。 - mjs
    3
    不对,没有阳光。在反驳像 JavaScript 核心功能这样牢不可破的内容之前,你应该先测试并深入理解它们。而且这并不是“我的解决方案”,自 JS 诞生以来,A.length = 0 就是基本语法。虽然可能有些含糊不清,但 A.length = 0 是清空数组的标准语法。如果你想要一个 A.clear 这样的方法,你可以使用这个标准语法轻松地将其添加到你的数组中:Array.prototype.clear = function(){this.length = 0}。玩得开心! - Bekim Bacaj
    20
    我从未说过这个解决方案是错误的。问题在于这整个线程完全没有必要。超过3000个投票表明,尝试弄清楚最佳方式应该足够成为EMCA破解者开发人员添加此类方法的有效案例。没有人应该经历弄清楚它。有三到四种不同的方法可以做到这一点。在某些基准测试中,长度解决方案比其他解决方案慢得多。此外,对于任何合理的开发人员来说,设置.length = 0的想法都不会令人满意。 - mjs
    11
    因此,我宁愿自己来搞清楚它。可以制定一种清晰的方法,但那只是丑陋的。我的观点是,该实现应该已经存在,这样超过10,000名开发人员不必花费数小时阅读仅此线程,更不用考虑所有其他花费更多时间进行基准测试的人了。 - mjs
    7
    我喜欢这个回答中使用现实世界隐喻的方式,令人印象深刻! - Victor
    显示剩余8条评论

    65

    性能测试:

    http://jsperf.com/array-clear-methods/3

    (说明:此页面是一个关于清空数组的方法性能测试的网页链接)
    a = []; // 37% slower
    a.length = 0; // 89% slower
    a.splice(0, a.length)  // 97% slower
    while (a.length > 0) {
        a.pop();
    } // Fastest
    

    10
    除非同时注明您使用的平台,否则添加百分比变化并没有太大用处。在我的电脑上,使用Chrome 34,Pop只比[]稍微快一点,但在最新的Firefox中却更慢。 - Matt Styles
    2
    当然,创建一个新的数组实例比循环和弹出它要快...所以如果在Chrome中弹出更快,那么创建新数组实例就有问题。 - HellBaby
    5
    这个Chrome浏览器下的测试结果肯定有些问题。怎么可能弹出循环比其他三种解决方案快那么多呢? - chqrlie
    11
    不是的,这种方法是最慢的。基准测试有缺陷。 - Philippe Leybaert
    23
    请删除此答案,因为它是错误的,并且链接到一个毫无意义的有缺陷的测试作为虚假证据。 - James Wakefield
    显示剩余2条评论

    37
    你可以将以下代码添加到你的JavaScript文件中,以使你的数组可以"清空":
    Array.prototype.clear = function() {
        this.splice(0, this.length);
    };
    

    然后你可以像这样使用它:

    var list = [1, 2, 3];
    list.clear();
    

    或者如果您想确保不会破坏任何东西:

    if (!Array.prototype.clear) {
        Array.prototype.clear = function() {
           this.splice(0, this.length);
        };
    }
    

    很多人认为你不应该修改原生对象(例如数组),我倾向于同意。在决定如何处理时,请谨慎使用。


    2
    @naomik,你能解释一下为什么做这样的事情是不被赞同的吗? - Undefined
    21
    修改 JavaScript 原生函数如 Array 和 String 是被不赞成的。你可能会重载已经存在的函数并破坏对象类。可能有一些不常见的 JavaScript 引擎已经有了 clear() 函数并期望它以不同的方式运行。我只能说要小心操作。 - Design by Adrian
    如何解决在数组成员上进行foreach循环时,突然开始包含一个“clear”键的问题? - ErikE

    29

    您可以轻松创建一个函数来为您执行此操作,更改长度甚至将其添加到本机数组中作为可重用的remove()函数。

    想象一下您有这个数组:

    var arr = [1, 2, 3, 4, 5]; //the array
    

    好的,只需简单地运行这个命令:

    arr.length = 0; //change the length
    

    结果如下:

    [] //result
    

    清空数组的简单方法...

    另外还有一种使用循环的方式,虽然不是必要的,但这也是另一种做法:

    /* could be arr.pop() or arr.splice(0)
    don't need to return as main array get changed */
    
    function remove(arr) {
      while(arr.length) {
        arr.shift(); 
      }
    }
    

    还有一些你可以考虑的巧妙方法,例如像这样:

    arr.splice(0, arr.length); //[]
    

    如果 arr 数组有 5 个项目,则它将从索引 0 开始剪切 5 个项目,这意味着数组中不会再剩下任何内容。

    还有其他方法,比如简单地重新分配数组,例如:

    arr = []; //[]
    

    如果你看一下数组函数,还有很多其他的方法可以做到这一点,但最推荐的方法可能是改变数组的长度。

    正如我在第一个位置所说的那样,你也可以原型化remove(),因为它就是你问题的答案。你可以简单地选择上述方法之一,并将其原型化到JavaScript中的Array对象,类似于:

    Array.prototype.remove = Array.prototype.remove || function() {
      this.splice(0, this.length);
    };
    

    您可以直接调用以下函数来清空JavaScript应用程序中的任何数组:
    arr.remove(); //[]
    

    arr.length = 0; //改变长度 - Therichpost

    20

    如果您正在使用

    a = []; 
    

    那么你正在将新的数组引用分配给a,如果a中的引用已经分配给任何其他变量,则它也不会清空该数组,因此垃圾收集器不会收集该内存。

    例如:

    var a=[1,2,3];
    var b=a;
    a=[];
    console.log(b);// It will print [1,2,3];
    
    或者
    a.length = 0;
    

    当我们指定a.length时,只是重新设置了数组的范围,其余数组元素的内存将由垃圾收集器连接。

    这两种解决方案更好。

    a.splice(0,a.length)
    

    while(a.length > 0) {
        a.pop();
    }
    

    根据kenshou.html之前的回答,第二种方法更快。


    11
    除了在a.length上错误之外,我不认为这个新答案在主题讨论中添加了什么内容? - Bergi
    @Bergi,我只想关注数组的实际内存表示。 - Laxmikant Dange
    你有什么来源可以证实哪些JS引擎在执行a.length=0;时会创建一个新的数组?对于a.length=500;a.length=4;,这些引擎会如何处理? - joeytwiddle
    我在大多数浏览器上尝试了它,如IE,Firefox,Chrome,它正在创建新数组。如果您将长度设置为大于0,则会创建一个带有未定义元素的数组,即它只会保留一些内存位置。 - Laxmikant Dange
    2
    var a = [1]; var b = a; a.length = 0; console.log(b) 打印出 Array [ ],所以在我看来它似乎并没有创建一个新的数组。 - John Montgomery

    19

    对于while/pop和shift方法的性能问题,有很多混淆和错误的信息存在于答案和评论中。正如预期的那样,while/pop方案具有最差的性能。实际上发生的情况是,设置仅在运行循环中的代码段的每个样本时运行一次。例如:

    var arr = [];
    
    for (var i = 0; i < 100; i++) { 
        arr.push(Math.random()); 
    }
    
    for (var j = 0; j < 1000; j++) {
        while (arr.length > 0) {
            arr.pop(); // this executes 100 times, not 100000
        }
    }
    

    我创建了一个新的测试,可以正确地工作:

    http://jsperf.com/empty-javascript-array-redux

    注意:即使在这个测试版本中,你实际上看不到真正的差异,因为克隆数组占据了大部分测试时间。它仍然显示出splice是清空数组的最快方法(不考虑[],因为虽然它是最快的,但它实际上并没有清空现有的数组)。


    非常好的观点!我会用正确的基准测试结果更新原始答案。 - Philippe Leybaert
    我简直不敢相信没有人发现那个基准测试错误。有超过五十万的浏览量,你会期望有人注意到它。干得好,Diadistis。 - Philippe Leybaert

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