数组的33种基本方法

326 阅读14分钟

es3方法

join()

数组转字符串

Array.join()方法将数组中的所有元素都转化为字符串并连接在一起,返回最后生成的字符串。可以指定一个可选的字符串在生成的字符串中分割数组的各个元素,如果不指定分隔符,默认使用逗号,如一下代码所示:

var a = [1,2,3]; //创建一个包含三个元素的数组
a.join();        // => "1,2,3"
a.join(" ")      //=> "1 2 3"

reverse()

数组倒序

Array.reverse()方法将数组中的元素颠倒顺序,返回逆序的数组。他采取了替换,换句话说,它不通过重新排列的元素创建新的数组,而是在原有的数组中重新排列它们。例:

var a = [1,2,3];            //创建一个包含三个元素的数组
console.log(a.reverse());   // => [3,2,1]

sort()

数组排序

Array.sort()方法将数组中的元素排序并返回排序后的数组。当不带参数调用sort()时,数组元素以字母表顺序排序

var a = new Array("banana", "cherry", "apple");
console.log(a.sort());  // =>["apple", "banana", "cherry"]

为了按照其他方式非字母表顺序进行排序,必须给sort()方法传递一个比较函数。该函数决定了他的两个参数在排好序的数组中的先后顺序。假设第一个参数应该在前,比较函数应该放回一个小于0的数值。反之,假设第一个参数应该在后,函数应该返回一个大于0的数值。并且假设两个值相等,函数应该返回0。

var a = [33, 4, 1111, 222];
console.log(a.sort()); //=>[1111, 222, 33, 4]
var b = a.sort(function(a,b){return a-b;});
console.log(b);  //=>[4, 33, 222, 1111]
var c = a.sort(function(a,b){ return b-a;});
console.log(c);  //=>[1111, 222, 33, 4]

concat()

数组连接

Array.concat()方法创建并返回一个新数组,他的元素包括调用concat()的原始数组的元素和concat()的每个参数。如果这些参数中的任意一个自身是数组,则连接的是数组的元素,而非数组本身。但要注意,concat()不会递归扁平化数组的数组,concat()也不会修改调用的数组。例:

var a = [1,2,3];
console.log(a.concat(4,5));  //=>[1, 2, 3, 4, 5]
console.log(a.concat([4,5])); //=>[1, 2, 3, 4, 5]
console.log(a.concat([4,5],[6,7]));  //=>[1, 2, 3, 4, 5, 6, 7]
console.log(a.concat(4,[5,[6,7]]));  //=>[1, 2, 3, 4, 5, [6, 7]]

slice()

数组截取

Array.slice()方法返回指定数组的一个片段或子数据。它的两个参数分别指定了片段的开始和结束的位置。返回的数组包含第一个参数指定的位置和所有到但不包含第二个参数指定的位置之间的所有数组元。如果只指定一个参数,返回的数组将包含从开始位置到数组结尾的所有元素。如参数出现负数,他表示相对于数组中最后一个元素的位置。

var a = [1,2,3,4,5];
a.slice(0,3)  //=>[1,2,3]
a.slice(3) //=>[4,5]
a.slice(1,-1) //=>[2,3,4]
a.slice(-3,-2); //=>[3]   

splice()

截取,删除,添加元素

Array.splice()方法是在数组中插入或删除元素的通用方法。不同于slice()和concat(),splice()会修改调用的的数组,注意,splice()和slice()拥有非常相似的名字,但他们的功能却有本质的区别。

splice()能够从数组中删除元素,插入元素到数组中或者同时完成这两种操作,在插入或删除点之后的数组元素会根据需要增加或减少它们的索引值,因此数组的其他部分仍然是保持连续的,splice()的第一个参数指定了插入和(或)删除的起始位置。第二个参数指定了应该从数组中删除的元素的个数。如果省略了第二个参数,从起点开始到数组的结尾的所有元素都将被删除。splice()返回一个由删除元素组成的数组,或者如果没有删除元素就返回一个空数组。例如:

var a = [1,2,3,4,5,6,7,8];
console.log(a.splice(4));  //=>[5, 6, 7, 8]
console.log(a);   //=>[1, 2, 3, 4]
console.log(a.splice(1,2)); //=>[2, 3]
console.log(a); //=>[1, 4]
console.log(a.splice(1,1));  //=>[4]
console.log(a);  //=>[1]

splice()的前两个参数指定了需要删除的数组元素,紧随其后的任意个数的参数指定了需要插入到数组中的元素,从第一个参数指定的位置开始插入。例如:

var a = [1,2,3,4,5];
console.log(a.splice(2,0,"a","b"));  //=>[]
console.log(a);  //=>[1, 2, "a", "b", 3, 4, 5]
console.log(a.splice(2,2,[1,2],3));  //=>["a", "b"]
console.log(a)  //=>[1, 2, [1,2], 3, 3, 4, 5]

注意区别于concat(),splice()会插入数组本身而非数组的元素。

push()和pop()

尾部添加元素,移除元素

push()和pop()方法允许将数组当做栈来使用。push()方法是在数组的尾部添加一个或多个元素,并返回数组新的长度。pop()方法则相反,它删除数组的最后一个元素,减小数组的长度并返回它删除的值。注意两个方法都修改并替换原始数组而非生成一个修改版的新数组。组合使用push()和pop()能够用javascript数组实现先进后出的栈。例如:

var stack = [];     //stack:[]
stack.push(1,2);    //stack:[1,2] 返回:2
stack.pop();        //stack:[1]   返回:2
stack.push(3);      //stack:[1,3] 返回:2
stack.pop();        //stack:[1]   返回:3
stack.push([4,5]);  //stack:[1.[4,5]] 返回:2
stack.pop();        //stack:[1]   返回:[4,5]
stack.pop();        //stack:[]    返回:1

unshift()和shift()

头部添加,移除元素

unshift()和shift()方法非常类似于push()和pop(),不一样的是前者是在数组头部而非尾部进行元素的插入和删除操作。unshift()在数组的头部添加一个或多个元素,并将已存在的元素移动到更高索引的位置来获得足够的空间,最后返回数组新的长度。shift()删除数组的第一个元素并将其返回,然后把所有随后的元素下移一个位置来填补数组头部的空缺。例如:

var a = [];                         //a:[]
console.log(a.unshift(1));          //=>1
console.log(a);                     //=>[1]
console.log(a.unshift(22));         //=>2
console.log(a);                     //=>[22,1]
console.log(a.shift(1));            //=>22
console.log(a);                     //=>[1]
console.log(a.unshift(3,[4,5]));    //=>3    
console.log(a);                     //=>[3,[4,5],1]
console.log(a.shift(1));            //=>3
console.log(a);                     //=>[[4,5],1]
console.log(a.shift(1));            //=>[4,5]
console.log(a);                     //=>[1]
console.log(a.shift(1));            //=>1

toString()和toLocalString()

数组和其他javascript对象一样拥有toString()方法,该方法将其每个元素转化为字符串,并且输出用逗号分隔的字符串列表。输出不包括方括号或其他任何形式的包裹数组中的分隔符。例如:

console.log([1,2,3].toString());        //=>"1,2,3"
console.log(['a','b','c'].toString());  //=>"a,b,c"
console.log([1,[2,"c"]].toString());    //=>"1,2,c"

注意,这里与不使用任何参数调用join()方法返回的字符串是一样的。

es5方法

foreach()

遍历数组

forEach()方法从头至尾遍历数组,为每个元素调用指定的函数。如上所诉,传递的函数座位forEach()的第一个参数。然后forEach()使用三个参数调用该函数:数组元素、元素的索引和数组本身。如果只关心数组元素的值,可以编写只有一个参数的函数----额外的参数将被忽略;

var data = [1,2,3,4,5];  //要求和的数组
// 计算数组元素的和
var sum = 0;
data.forEach(function(value){sum += value});
console.log(sum);  //=>15
// 每个数组元素自增1
data.forEach(function(v,i,a){ a[i] = v + 1; });
console.log(data)   //=>[2, 3, 4, 5, 6]

注意,forEach()无法在所有元素都传递给调用的函数之前终止遍历。也就是说,没有像for循环中使用响应的break语句。如果要提前终止,必须把forEach()方法放在一个try块中,并且抛出一个异常。如果forEach()调用的函数抛出foreach.break异常,循环会提前终止:

function foreach(a,f,t){
    try{a.foreach(f,t)}
    catch(e){
        if(e === foreach.break) return;
        else throw e;
    }
}
foreach.break = new Error("StopIteration");

map()

遍历数组

map()方法将调用的数组每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。例如:

a = [1,2,3];
b = a.map(function(x){ return x*x;});
console.log(b)  //[1,4,9]

传递给map()的函数的调用方式和传递给forEach()的函数的调用方式一样。但传递给map()的函数应该有返回值。注意map()返回的是新数组:它不修改调用的数组。如果是稀疏数组,返回的也是相同方式的稀疏数组;它具有相同的长度,相同的缺失元素。

filter()

数组过滤

fliter()方法返回的元素是调用的数组的一个子集。传递的函数是用来逻辑判定的,该函数返回true或false。调用判断函数就像调用forEach()和map()一样。如果返回值为true或能转化为true的值,那么传递给判定函数的元素就是这个子集的成员,他将被添加到一个座位返回值的数组中。例如:

let a = [5,4,3,2,1];

smallValue = a.filter(function(x){return x < 3 });
everyother = a.filter(function(x,i){return i%2 == 0});

console.log(smallValue); //=>[2,1]
console.log(everyother); //=>[5,3,1]

注意,filter()会跳过稀疏数组中缺少的元素,他返回的数组总是稠密的。为了压缩稀疏数组的空缺,代码如下:

var dense = sparse.filter(function(){ return true; });

甚至,压缩空缺并删除undefined和null元素,可以这样使用filter();

a = a.filter(function(x){ return x !=undefined&&x !=null; });

every()和some()

every()和some()方法是数组的逻辑判定,他们对数组元素应用指定的函数进行判断,返回true或false。

every()方法就行数学中的“针对所有”的量词∀:当且仅当针对数组中的所有元素调用判断函数都返回true,他才会返回true。

let a = [1,2,3,4,5];
console.log(a.every(function(x){return x < 10}))  //=>true
console.log(a.every(function(x){return x%2 ===0;}))   //=>false

some()方法就像数学中的“存在”量词∃,当数组中至少有一个元素调用判定函数返回true,它就返回true;并且当且仅当数值中的所有元素调用判定函数都返回false,它才返回false。

let a = [1,2,3,4,5];
console.log(a.some(function(x){return x%2 === 0;});) 
console.log(a.some(isNaN));

注意,一旦every()和some()确认该返回什么值它们就会停止遍历数组元素。some()在判定函数第一次返回true后就返回true,但如果判定函数一直返回false,它将会遍历整个数组。every()恰好相反。它在判定第一次返回false后就返回false,但如果判定函数一直返回true,他将遍历整个数组。注意,根据数学上的惯例,在空数组上调用时,every()返回true,some()返回false。

reduce()和reduceRight()

reduce()和reduceRight()方法使用指定的函数将数组元素进行组合,生成单个值。这在函数式编程中是常见的操作,也可以成为“注入”和“折叠”。举例说明它是如何工作的:

var a = [1,2,3,4,5];
var sum = a.reduce(function(x,y){ return x+y },0);  //数组求和
console.log(sum);  //=>15
var product = a.reduce(function(x,y){ return x*y },1);  //数组求积
console.log(product); //=>120
var max = a.reduce(function(x,y){ return (x>y)?x:y; });  //求最大值
console.log(max); //=>5

reduce()需要两个参数。第一个是执行化简操作的函数。化简函数的任务就是用某种方法把两个值组合或简化为一个值,并返回化简后的值。在上述例子中,函数通过加法、乘法或取最大值的方法组合两个值。第二个(可选)的参数是一个传递给函数的初始值。

reduce()使用的函数与forEach()和map()使用的函数不同。比较熟悉的是,数组元素、元素的索引和数组本身将作为第2~4个参数传递给函数。第一个参数是到目前为止的化简操作累积的结果。第一个调用函数时,第一个参数是初始值,他是传递给reduce()的第二个参数。在接下来的调用中,这个值就是上一次化简函数的返回值。在上面的第一个例子中,第一次调用化简函数时的参数是0和1。将两者相加并返回1.再次调用时的参数是1和2,它返回3.然后它计算3+3=6、6+4=10,最后计算10+5=15.最后的值是15,reduce()返回这个值。

indexOf()和lastIndexOf()

获取数组索引值

indexOf()和lastIndexOf()搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回-1,indexOf()从头至尾搜素,而lastIndexOf()则反向搜索。

var a = [0,1,2,1,0];
console.log(a.indexOf(1));          //=>1 a[1]是1
console.log(a.lastIndexOf(1));      //=>3 a[3]是1
console.log(a.indexOf(3));          //=>-1 没有值为3的元素

不同于其他方法,indexOf()和lastIndexOf()方法不接受一个函数作为其参数。第一个参数是需要搜索的值,第二个参数是可选的,它指定数组的一个索引,从那里开始搜索,如果省略该参数,indexOf()从头开始搜索,而lastIndexOf()从末尾开始搜索。第二个参数可以是负数,它代表相对数组末尾的偏移量,对于splice()方法。例如,-1指定数组的最后一个元素。

如下函数在一个数组中搜索指定的值并返回包含所有匹配的数组索引的一个数组。它展示了如何运用indexOf()的第二个参数来查找除了第一个以外的匹配值。

let arr = [1,2,3,4,5,6,5,4,3,2,1,2,3,4,5];   
function findall(a,x) {
    var results = [],
        len = a.length,
        pos = 0;
    while(pos < len) {
        pos = a.indexOf(x , pos);
        if(pos === -1){break};
        results.push(pos);
        pos++
    }  
    return results;
}
console.log(findall(arr,3));  //=>[2,8,12]
注意,字符串也有indexOf()和lastIndexOf()方法,它们和数组方法的功能类似

es6方法

from()

Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。

下面是一个类似数组的对象,Array.from将它转为真正的数组。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']

实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

// NodeList对象
let ps = document.querySelectorAll('p');
Array.from(ps).filter(p => {
  return p.textContent.length > 100;
});

// arguments对象
function foo() {
  var args = Array.from(arguments);
  // ...
}

上面代码中,querySelectorAll方法返回的是一个类似数组的对象,可以将这个对象转为真正的数组,再使用filter方法。

of()

Array.of方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

copyWithin()

数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)

它接受三个参数。

target(必需):从该位置开始替换数据。如果为负值,表示倒数。

start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。

end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。 这三个参数都应该是数值,如果不是,会自动转为数值。

[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]

find() 和 findIndex()

数组实例的find方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined。

[1, 4, -5, 10].find((n) => n < 0)
// -5

fill()

fill方法使用给定值,填充一个数组。

['a', 'b', 'c'].fill(7)
// [7, 7, 7]

new Array(3).fill(7)
// [7, 7, 7]

entries(),keys()和values()

ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历。

let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

includes()

Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法。

[1, 2, 3].includes(2)     // true
[1, 2, 3].includes(4)     // false
[1, 2, NaN].includes(NaN) // true

float(),flatMap()

数组的成员有时还是数组,Array.prototype.flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。

[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]