添加元素
在数组中添加和删除元素也很容易,但有时也会很棘手。假如我们有一个数组numbers,初始化成了0到9。
1 | let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; |
在数组末尾插入元素
如果想要给数组添加一个元素(比如10),只要把值赋给数组中最后一个空位上的元素即可。
1 | numbers[numbers.length] = 10; |
在JavaScript中,数组是一个可以修改的对象。如果添加元素,它就会动态增长。在C和Java等其他语言里,我们要决定数组的大小,想添加元素就要创建一个全新的数组,不能简单地往其中添加所需的元素。
使用push方法
另外,还有一个push方法,能把元素添加到数组的末尾。通过push方法,我们能添加任意个元素。
1 | numbers.push(11); |
如果输出numbers的话,就会看到从0到13的值。
在数组开头插入元素
现在,我们希望在数组中插入一个新元素(数-1),不像之前那样插入到最后,而是放到数组的开头。为了实现这个需求,首先要腾出数组里第一个元素的位置,把所有的元素向右移动一位。我们可以循环数组中的元素,从最后一位(长度值就是数组的末尾位置)开始,将对应的前一个元素(i-1)的值赋给它(i),依次处理,最后把我们想要的值赋给第一个位置(索引0)上。我们可以将这段逻辑写成一个函数,甚至将该方法直接添加在Array的原型上,使所有数组的实例都可以访问到该方法。下面的代码表现了这段逻辑。
1 | Array.prototype.insertFirstPosition = function(value) { |
下图描述了我们刚才的操作过程。


使用unshift方法
在JavaScript里,数组有一个方法叫unshift,可以直接把数值插入数组的开头(此方法背后的逻辑和insertFirstPosition方法的行为是一样的)。
1 | numbers.unshift(-2); |
那么,用unshift方法,我们就可以在数组的开始处添加值-2,然后添加-3、-4等。这样数组就会输出数-4到13。
删除元素
目前为止,我们已经学习了如何给数组的开始和结尾位置添加元素。下面来看一下怎样从数组中删除元素。
从数组末尾删除元素pop
要删除数组里最靠后的元素,可以用pop方法。
1 | numbers.pop(); |
通过
push和pop方法,就能用数组来模拟栈,你将在下一章看到这部分内容。
现在,数组输出的数是-4到12,数组的长度是17。
从数组开头删除元素shift
如果要移除数组里的第一个元素,可以用下面的代码。
1 | for (let i = 0; i < numbers.length; i++) { |
下图呈现了这段代码的执行过程。


我们把数组里所有的元素都左移了一位,但数组的长度依然是17,这意味着数组中有额外的一个元素(值是undefined)。在最后一次循环里,i+1引用了数组里还未初始化的一个位置。在Java、C/C+或C#等一些语言里,这样写可能会抛出异常,因此不得不在numbers.length- 1处停止循环。
可以看到,我们只是把数组第一位的值用第二位覆盖了,并没有删除元素(因为数组的长度和之前还是一样的,并且多了一个未定义元素)。
要从数组中移除这个值,还可以创建一个包含刚才所讨论逻辑的方法,叫作removeFirstPosition。但是,要真正从数组中移除这个元素,我们需要创建一个新的数组,将所有不是undefined的值从原来的数组复制到新的数组中,并且将这个新的数组赋值给我们的数组。要完成这项工作,也可以像下面这样创建一个reIndex方法。
1 | Array.prototype.reIndex = function(myArray) { |
使用shift方法
要删除数组的第一个元素,可以用shift方法实现。
1 | numbers.shift(); |
假如本来数组中的值是从-4到12,长度为17。执行了上述代码后,数组就只有-3到12了,长度也会减小到16。
JavaScript的数组方法参考
在JavaScript里,数组是经过改进的对象,这意味着创建的每个数组都有一些可用的方法。数组很有趣,因为它十分强大,并且相比其他语言中的数组,JavaScript中的数组有许多很好用的方法。这样就不用再为它开发一些基本功能了,例如在数据结构的中间添加或删除元素。
下表详述了数组的一些核心方法,其中的一些我们已经学习过了。
| 方法 | 描述 |
|---|---|
concat |
连接2个或更多数组,并返回结果 |
every |
对数组中的每个元素运行给定函数,如果该函数对每个元素都返回true,则返回true |
filter |
对数组中的每个元素运行给定函数,返回该函数会返回true的元素组成的数组 |
forEach |
对数组中的每个元素运行给定函数。这个方法没有返回值 |
join |
将所有的数组元素连接成一个字符串 |
indexOf |
返回第一个与给定参数相等的数组元素的索引,没有找到则返回-1 |
lastIndexOf |
返回在数组中搜索到的与给定参数相等的元素的索引里最大的值 |
map |
对数组中的每个元素运行给定函数,返回每次函数调用的结果组成的数组 |
reverse |
颠倒数组中元素的顺序,原先第一个元素现在变成最后一个,同样原先的最后一个元素变成了现在的第一个 |
slice |
传入索引值,将数组里对应索引范围内的元素作为新数组返回 |
some |
对数组中的每个元素运行给定函数,如果任一元素返回true,则返回true |
sort |
按照字母顺序对数组排序,支持传入指定排序方法的函数作为参数 |
toString |
将数组作为字符串返回 |
valueOf |
和toString类似,将数组作为字符串返回 |
我们已经学过了push、pop、shift、unshift和splice方法。
使用reduce方法
最后是reduce方法。reduce方法接收一个有如下四个参数的函数:previousValue、currentValue、index和array。因为index和array是可选的参数,所以如果用不到它们的话,可以不传。这个函数会返回一个将被叠加到累加器的值,reduce方法停止执行后会返回这个累加器。如果要对一个数组中的所有元素求和,这就很有用。下面是一个例子。
1 | numbers.reduce((previous, current) => previous + current); |
输出将是120
ECMAScript 6和数组的新功能
第1章提到过,ECMAScript 2015(ES6或ES2015)和更新的规范(2015+)给JavaScript语言带来了新的功能。
下表列出了ES2015和ES2016新增的数组方法。
| 方法 | 描述 |
|---|---|
@@iterator |
返回一个包含数组键值对的迭代器对象,可以通过同步调用得到数组元素的键值对 |
copyWithin |
复制数组中一系列元素到同一数组指定的起始位置 |
entries |
返回包含数组所有键值对的@@iterator |
includes |
如果数组中存在某个元素则返回true,否则返回false。E2016新增 |
find |
根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素 |
findIndex |
根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素在数组中的索引 |
fill |
用静态值填充数组 |
from |
根据已有数组创建一个新数组 |
keys |
返回包含数组所有索引的@@iterator |
of |
根据传入的参数创建一个新数组 |
values |
返回包含数组中所有值的@@iterator |
除了这些新的方法,还有一种用for...of循环来迭代数组的新做法,以及可以从数组实例得到的迭代器对象。
使用@@iterator对象
ES2015还为Array类增加了一个@@iterator属性,需要通过Symbol.iterator来访问。代码如下。
1 | let iterator = numbers[Symbol.iterator](); |
然后,不断调用迭代器的next方法,就能依次得到数组中的值。numbers数组中有15个值,因此需要调用15次iterator.next().value。
我们可以用下面的代码来输出numbers数组中的15个值。
1 | iterator = numbers[Symbol.iterator](); |
数组中的所有值都迭代完之后,iterator.next().value会返回undefined。
排序元素
通过本书,我们能学到如何编写最常用的搜索和排序算法。其实,JavaScript里也提供了一个排序方法和一组搜索方法。让我们来看看。
首先,我们想反序输出数组numbers(它本来的排序是1, 2, 3, 4, ..., 15)。要实现这样的功能,可以用reverse方法,然后数组内元素就会反序。
1 | numbers.reverse(); |
现在,输出numbers的话就会看到[15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]。然后,我们使用sort方法。
1 | numbers.sort(); |
然而,如果输出数组,结果会是[1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]。看起来不大对,是吧?这是因为sort方法在对数组做排序时,把元素默认成字符串进行相互比较。
我们可以传入自己写的比较函数。因为数组里都是数,所以可以像下面这样写。
1 | numbers.sort((a, b) => a - b); |
在b大于a时,这段代码会返回负数,反之则返回正数。如果相等的话,就会返回0。也就是说返回的是负数,就说明a比b小,这样sort就能根据返回值的情况对数组进行排序。
之前的代码也可以表示成如下这样,会更清晰一些。
1 | function compare(a, b) { |
这是因为JavaScript的sort方法接收compareFunction作为参数,然后sort会用它排序数组。在这个例子里,我们声明了一个用来比较数组元素的函数,使数组按升序排序。
自定义排序
我们可以对任何对象类型的数组排序,也可以创建compareFunction来比较元素。例如,对象Person有名字和年龄属性,我们希望根据年龄排序,就可以这么写。
1 | const friends = [ |
在这个例子里,最后会输出Ana(20), Chris(25), John(30)。
字符串排序
假如有这样一个数组。
1 | let names = ['Ana', 'ana', 'john', 'John']; |
你猜会输出什么?答案如下所示。
1 | ["Ana", "John", "ana", "john"] |
既然a在字母表里排第一位,为何ana却排在了John之后呢?这是因为JavaScript在做字符比较的时候,是根据字符对应的ASCII值来比较的。例如,A、J、a、j对应的ASCII值分别是65、74、97、106。
虽然a在字母表里是最靠前的,但J的ASCII值比a的小,所以排在了a前面。
现在,如果给sort传入一个忽略大小写的比较函数,将输出["Ana", "ana", "John", "john"]。
1 | names = ['Ana', 'ana', 'john', 'John']; // 重置数组的初始状态 |
在这种情况下,sort函数不会有任何作用。它会按照现在的大小写字母顺序排序。
如果希望小写字母排在前面,那么需要使用localeCompare方法。
1 | names.sort((a, b) => a.localeCompare(b)); |
输出结果将是["ana", "Ana", "john", "John"]。
假如对带有重音符号的字符做排序的话,也可以用localeCompare来实现。
1 | const names2 = ['Maève', 'Maeve']; |
最后输出的结果将是["Maeve", "Maève"]。