数组方法简介
数组总共有22种方法,本文将其分为以下几类来进行详细介绍。
原数组变化:push() pop() shift() unshift() reverse() sort() splice()
原数组不变:concat() slice()
1.对象继承方法
数组是一种特殊的对象,它继承了对象Object中的toString()、toLocaleString()和valueOf()方法
toString() :
返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串。(类似后面join()方法的功能)
[注意] 该方法的返回值与调用join()方法不传参数返回的字符串相同
var arr = [1,2,3] console.log(arr.toString()) // 1,2,3
alert(arr) // 1,2,3 // alert()要接收字符串参数,它会在后台调用toString()方法,会得到与toString()方法相同的结果
toLocaleString() :
toString()方法的本地化版本,经常返回与toString()方法相同的值,但也不总是如此。
var car1 = { toLocaleString: function(){ return 'BMW'; }, toString: function(){ return 'VOLVO'; } }; var car2 = { toLocaleString: function(){ return 'BENTLEY'; }, toString: function(){ return 'MAZDA'; } }; var car = [car1,car2]; console.log(car.toString()); // VOLVO,MAZDA console.log(car.toLocaleString()); // BMW,BENTLEY // 注释 数组 car 也是特殊对象,本身自带 toString()和toLocaleString() 属性,但对象car1与car2同时对两个属性重写 // 因此,调用数组car的这两个属性就是对应调用car1和car2的这两个属性。
如果数组中的某一项的值是null或者undefined,则该值在toLocaleString()和toString()方法返回的结果中以空字符串表示
var arr = [1, undefined, null, 3] console.log(arr.toString()) // 1,,,3
valueOf() :
方法返回数组对象本身
var a = [1, 2, 3]; console.log(a.valueOf());// [1, 2, 3] console.log(a.valueOf() instanceof Array);//true
【注释】用instanceof 来检测对象a是不是对象b构造出来的对象,返回布尔值
2.数组转换方法
Array.join()
该方法方法是String.split()方法的逆向操作,后者是将字符串分割成若干块来创建一个数组;
join()方法只接收一个参数,用作分隔符的字符串,然后返回包含所有数组项的字符串
【注意】若join()方法的参数是undefined,标准浏览器以逗号为分隔符返回字符串,而IE7-浏览器以'undefined'为分隔符返回字符串
如果数组中的某一项的值是null或者undefined,则该值在join()方法返回的结果中以空字符串表示
var a = [1,2,3]; console.log(a.join(undefined)); //标准浏览器为'1,2,3';IE7-浏览器为'1undefined2undefined3'
var colors = [1,undefined,2,null,3];
console.log(colors.join());//'1,,2,,3'
【注意】若对象没有length属性,就不是类数组,也就不能调用数组的方法
3.栈和队列方法(添加删除)
push() 在数组末尾添加任意个项并返回新数组长度。
var arrA = []; var arrB = []; var arrC = []; arrA.push('a'); arrB.push('b'); arrC.push('c'); console.log(arrA, arrB) // ["a"], ["b"] // 可以使用apply方法合并两个数组 Array.prototype.push.apply(arrA, arrC) console.log(arrA) // ["a", "c"] // 使用call方法,则会把数组arrB整体看成一个参数 arrC = Array.prototype.push.call(arrB, arrC) console.log(arrB) // ["a", ["c"]]
pop() 移除数组中的最后一个项并返回该项,同时数组的长度减1。
var arr = ['a', 'b', 'c'];
var result = arr.pop();
console.log(arr, result); // ["a", "b"], "c"
arr = [];
result = arr.pop(); // 对空数组使用pop()方法,不会报错,而是返回undefined
console.log(arr, result); // [], undefined
unshift() 在数组前端添加任意个项并返回新数组长度。
var arr = ['a', 'b', 'c']; var result = arr.unshift('o'); console.log(arr, result); // ["o", "a", "b", "c"] , 4 result = arr.unshift('e', 'f', 'g');// 当使用多个参数调用unshift()时,参数是一次性插入的而非一次一个地插入。 console.log(arr, result); // ["e", "f", "g", "o", "a", "b", "c"] , 7
[注意]在IE7-浏览器中,unshift()方法返回的总是undefined
shift() 移除数组中的第一个项并返回该项,同时数组的长度减1。
var arr = ['a', 'b', 'c'];
var result = arr.shift();
console.log(arr, result); // ["b", "c"], "a"
arr = [];
result = arr.shift(); // 对空数组使用shift()方法,不会报错,而是返回undefined
console.log(arr, result); // [], undefined
【注意】以上四种方法都改变原数组,原数组改变为变化后的数组。
4.数组排序方法
数组中存在两个可以直接用来重排序的方法: reverse()和sort()
reverse() 方法用于反转数组的顺序,返回经过排序之后的数组;而原数组顺序也发生改变
var array = [1,2,4,3,5]; console.log(array,array.reverse());//[5,3,4,2,1] [5,3,4,2,1] 原数组也改变
sort() 默认是按字符串升序排列数组项,sort方法会调用每个数组项的toString()方法,
然后比较得到的字符串排序,返回经过排序之后的数组,而原数组顺序也发生改变。(不接受参数)
如果数组包含undefined元素,它们会被排到数组的尾部
var array = [1,2,4,3,5]; console.log(array,array.reverse());//[5,3,4,2,1] [5,3,4,2,1] 原数组也改变 var array = [1,5,10,20]; console.log(array,array.sort());//[1, 10, 20, 5] [1, 10, 20, 5] var array = ['3str',3,2,'2']; console.log(array,array.sort());//[2, "2", 3, "3str"] [2, "2", 3, "3str"] var array = [undefined,'3',3,'2',2]; console.log(array,array.sort());//[2, "2", "3", 3, undefined] (5) [2, "2", "3", 3, undefined]
sort() 方法可以接受一个比较函数作为参数,以便指定哪个值在哪个值的前面。比较函数接收两个参数,
如果第一个参数应该位于第二个参数之前则返回一个负数,如果两个参数相等则返回0,如果第一个参数应该位于第二个参数之后则返回一个正数
function compare (value1, value2) { if(value1 < value2){ return -1; }else if(value1 > value2){ return 1; }else{ return 0; } } //当数字与字符串比较大小时,字符串'5px'会被转换成NaN,这样结果就是false,位置不变 var array = ['5px', 6, 50, -1,10]; console.log(array,array.sort(compare));//["5px", -1, 6, 10, 50] ["5px", -1, 6, 10, 50]
对于数值类型或valueOf()方法会返回数值类型的对象类型,比较函数可以简化
function compare(value1,value2){ return value1 - value2; }
【注意】以上两种方法都改变原数组,原数组改变为变化后的数组。
5.数组拼接方法
concat() 方法基于当前数组中的所有项创建一个新数组,先创建当前数组一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
所以concat()不影响原数组
var arr1 = [1, 2]; var arr3 = arr1.concat() console.log(arr3) // [1, 2]
如果不给concat()方法传递参数时,它只是复制当前的数组;返回当前数组的一个浅拷贝
(浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存;深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。)
var arr1 = [1, 2, [3]];
var arr3 = arr1.concat()
arr1[0] = '1' // 原数组值改变,对应新数组值不变
arr1[2][0] = '3' // 原数组值内的值改变,对应新数组也变
console.log(arr1, arr3) // ['1', 2, '3'] [1, 2, '3']
如果参数是一个或多个数组,则该方法会将这些数组中的每一项都添加到结果数组中;
var arr1 = [1, 2]; var arr2 = [3, 4]; var arr3 = [7, 8]; var arr4 = arr1.concat(arr2, arr3) console.log(arr4) // [1, 2, 3, 4, 7, 8]
如果传递的值不是数组,这些值就会被简单地添加到结果数组的末尾
var arr1 = [1, 2]; var num = 5; var str = 'tom' var arr4 = arr1.concat(num, str) console.log(arr4) // [1, 2, 5, "tom"]
concat()方法也可以用于将对象合并为数组,但是必须借助call()方法
var obj1 = {a: 1} var obj2 = {b: 2} var arr1 = Array.prototype.concat.call(obj1, obj2) console.log(arr1) // [{a: 1}, {b: 2}]
6.创建子数组方法
slice(start,end) 方法需要两个参数start和end,返回这个数组中从start位置到end(但不包含)位置的一个子数组;如果end为undefined或不存在,则返回从start位置到数组结尾的所有项;最后返回新数组,所以slice()不影响原数组。
var arr1 = [1, 2, 3, 4, 5, 6] var arr2 = arr1.slice(0, 3) // [1, 2, 3] var arr3 = arr1.slice(0) // 等价arr1.slice(0, undefined) [1, 2, 3, 4, 5, 6] var arr4 = arr1.slice(-2, -1) //(-2,-1)<=>(-2+6, -1+6)<=>(4,5) [5]
如果start是负数,则start = max(length + start,0) 取最大值;如果end是负数,则end = max(length + end,0) 取最大值
start和end无法交换位置;如果没有参数,则返回原数组(浅拷贝) 等价于: arr1.slice(0)
slice()方法涉及到Number()转型函数的隐式类型转换,当start被转换为NaN时,相当于start = 0;
var arr1 = [1, 2, 3, 4, 5, 6] var arr2 = arr1.slice(NaN) // [1, 2, 3, 4, 5, 6]
当end被转换为NaN时(end为undefined除外),则输出空数组
var arr1 = [1, 2, 3, 4, 5, 6] var arr3 = arr1.slice(0, NaN) // []
splice(start,number, item1,item2,...) 方法用于删除原数组的一部分成员,并可以在被删除的位置添加入新的数组成员,该方法会改变原数组
splice()返回一个由删除元素组成的数组,或者如果没有删除元素就返回一个空数组
var arr1 = [1, 2, 3, 4, 5, 6, 7, 8]; var arr2 = arr1.splice(); console.log(arr1, arr2); // [1, 2, 3, 4, 5, 6, 7, 8] []
splice()的第一个参数start指定了插入或删除的起始位置。如果start是负数,则start = max(length + start,0);如果start是NaN,则相当于start = 0
var arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
var arr2 = arr1.splice(0);
console.log(arr1, arr2); // [] [1, 2, 3, 4, 5, 6, 7, 8]
arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
var arr3 = arr1.splice(-2); // -2+8=6
arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
var arr4 = arr1.splice(-9); // max(-9+8, 0) = 0
console.log(arr3, arr4); // [7, 8] [1, 2, 3, 4, 5, 6, 7, 8]
第二个参数number指定了应该从数组中删除的元素的个数。如果省略第二个参数,从起始点开始到数组结尾的所有元素都将被删除。
如果number是负数或NaN或undefined,则number=0,因此不删除元素
var arr = [1,2,3,4,5,6,7,8]; console.log(arr,arr.splice(0,2));// [3,4,5,6,7,8] [1,2] arr = [1,2,3,4,5,6,7,8]; console.log(arr,arr.splice(10,2));// [1,2,3,4,5,6,7,8] [] arr = [1,2,3,4,5,6,7,8]; console.log(arr,arr.splice(1,100));// [1] [2,3,4,5,6,7,8] arr = [1,2,3,4,5,6,7,8]; console.log(arr,arr.splice(1,-5));//[1,2,3,4,5,6,7,8] [] arr = [1,2,3,4,5,6,7,8]; console.log(arr,arr.splice(1,NaN));//[1,2,3,4,5,6,7,8] [] arr = [1,2,3,4,5,6,7,8]; console.log(arr,arr.splice(1,undefined));//[1,2,3,4,5,6,7,8] []
如果后面还有更多的参数,则表示这些就是要被插入数组的新元素
var arr = [1,2,3,4,5]; console.log(arr,arr.splice(2,0,'a','b'));//[1,2,'a','b',3,4,5] [] arr = [1,2,3,4,5]; console.log(arr,arr.splice(2,2,[1,2],3));//[1,2,[1,2],3,5] [3,4]
7.数组位置方法
ES5为数组实例添加了两个位置方法:indexOf()、lastIndexOf()
浏览器支持
表格中的数字表示支持该方法的第一个浏览器的版本号。
所有主要浏览器都支持 indexOf()方法 和lastIndexOf()方法,但是 Internet Explorer 8 及 更早IE版本不支持该方法。
indexOf(search,start)方法接收search和start两个参数,返回search首次出现的位置,如果没有找到则返回-1
search参数表示要搜索的项;使用严格相等运算符(===)进行比较
start参数表示该搜索的开始位置,该方法会隐式调用Number()转型函数,将start非数字值(undefined除外)转换为数字。
若忽略该参数或该参数为undefined或NaN时,start = 0
var arr = ['a','b','c','d','e','a','b']; console.log(arr.indexOf('a',undefined));//0 console.log(arr.indexOf('a',NaN));//0 console.log(arr.indexOf('a',1));//5 console.log(arr.indexOf('a',true));//5 console.log(arr.indexOf('a',-1));//max(0,-1+7)=6; -1 console.log(arr.indexOf('a',-5));//max(0,-5+7)=2; 5 console.log(arr.indexOf('a',-50));//max(0,-50+7)=0; 0
var person = {name: 'Nicholas'}; var people = [{name: 'Nicholas'}]; var morePeople = [person]; console.log(people.indexOf(person));//-1,因为person和people[0]虽然值相同,但是是两个引用 console.log(morePeople.indexOf(person));//0,因为person和morepeople[0]是同一个引用 console.log(morePeople.indexOf({name: 'Nicholas'}));//-1,因为不是同一个引用
indexOf()方法兼容写法
if (typeof Array.prototype.indexOf() !== 'function') { Array.prototype.indexOf = function (searchStr, startIndex) { startIndex = startIndex || 0 for (var i = 0, len = this.length; i < len; i++) { if (i >= startIndex && this[i] === searchStr) return i } return -1 } }
lastIndexOf(search,start)方法接收search和start两个参数,返回search第一次出现的位置,如果没有找到则返回-1
search参数表示要搜索的项;使用严格相等运算符(===)进行比较
start表示该搜索的开始位置,该方法会隐式调用Number()转型函数,将start非数字值(undefined除外)转换为数。
若忽略该参数,start = -1;若该参数为undefined或NaN时,start = 0。
与字符串的lastIndexOf()方法不同,当search方法为负数时,search = max(0,length+search)
var arr = ['a','b','c','d','e','a','b']; console.log(arr.lastIndexOf('b'));//6 console.log(arr.lastIndexOf('b',undefined));//-1 console.log(arr.lastIndexOf('a',undefined));//0 console.log(arr.lastIndexOf('b',NaN));//-1 console.log(arr.lastIndexOf('b',1));//1 console.log(arr.lastIndexOf('b',-1));//max(0,-1+7)=6; 6 console.log(arr.lastIndexOf('b',-5));//max(0,-5+7)=2; 1 console.log(arr.lastIndexOf('b',-50));//max(0,-50+7)=0; -1
lastIndexOf()方法兼容写法
if (typeof Array.prototype.lastIndexOf !== 'function') { Array.prototype.lastIndexOf = function (searchElement, fromIndex) { var len = this.length; arguments.length === 1 ? fromIndex = -1 : fromIndex = fromIndex * 1 || 0; if (fromIndex < 0) fromIndex = Math.max(0, fromIndex + len) for (var k = len - 1; k > -1; k--) { if (k <= fromIndex && this[k] === searchElement) { return k; } } return -1; }; }
8.数组归并方法
浏览器支持
表格中的数字表示支持该方法的第一个浏览器版本号。
数组归并方法包括reduce()和reduceRight()方法两种,它们使用指定的函数将数组元素进行组合,生成单个值。这在函数式编程中是常见的操作,也可以称为“注入”和“折叠”
reduce() 方法需要两个参数。第一个是执行化简操作的函数。化简函数的任务就是用某种方法把两个值组合或化简为一个值,并返回化简后的值
化简函数接受四个参数,分别是:
【1】初始变量,默认为数组的第一个元素值。函数第一次执行后的返回值作为函数第二次执行的初始变量,依次类推
【2】当前变量,如果指定了第二个参数,则该变量为数组的第一个元素的值,否则,为第二个元素的值
【3】当前变量对应的元素在数组中的索引(从0开始)
【4】原数组对象
化简函数的这四个参数之中,只有前两个是必须的,后两个则是可选的
values.reduce(function(prev, cur, index, array){ //todo });
reduce()方法第二个(可选)的参数是一个传递给函数的初始值
var array = [1,2,3,4,5]; var sumArray = array.reduce(function(x,y){return x+y},0);//数组求和 var productArray = array.reduce(function(x,y){return x*y},1);//数组求积 var maxArray = array.reduce(function(x,y){return (x>y)?x:y;});//求最大值 console.log(sumArray, productArray, maxArray) // 15 120 5
array.reduce(function (prev, cur) { console.log(prev, cur); return prev + cur; }) // 1 2 // 3 3 // 6 4 // 10 5 // 最后结果是: 15
[注意]reduce()方法的返回结果类型和传入的初始值相同
array.reduce(function (prev, cur) {
console.log(prev, cur);
return prev + cur;
}, 0)
// 0 1
// 1 2
// 3 3
// 6 4
// 10 5
// 最后结果是: 15
array.reduce(function (prev, cur) { console.log(prev.sum, cur); prev.sum = prev.sum + cur; return prev }, {sum: 0}) // 0 1 // 1 2 // 3 3 // 6 4 // 10 5 // 最后结果是: {sum: 15}
reduce()方法兼容写法
if (typeof Array.prototype.reduce !== "function") { Array.prototype.reduce = function (callback, initialValue ) { var previous = initialValue, k = 0, length = this.length; if (typeof initialValue === "undefined") { previous = this[0]; k = 1; } if (typeof callback === "function") { for (k; k < length; k++) { /* 通过 hasOwnProperty 查找是否是对象本身的属性,而不是继承来的属性 */ this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this)); } } return previous; }; }
reduceRight()的工作原理和reduce()一样,不同的是它按照数组索引从高到低(从右到左)处理数组,而不是从低到高
var array = [1, 2, 3, 4, 5]; array.reduceRight(function (prev, cur, key) { console.log(prev, cur, key); return prev + cur; }, 0) // 0 5 4 // 5 4 3 // 9 3 2 // 12 2 1 // 14 1 0 // 最后结果是: 15
reduceRight()方法兼容写法
if (typeof Array.prototype.reduceRight !== "function") { Array.prototype.reduceRight = function (callback, initialValue ) { var length = this.length, previous = initialValue, k = length - 1; if (typeof initialValue === "undefined") { previous = this[k]; k--; } if (typeof callback === "function") { for (k; k > -1; k--) { /* 通过 hasOwnProperty 查找是否是对象本身的属性,而不是继承来的属性 */ this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this)); } } return previous; }; }
9.数组迭代方法
浏览器支持
表格中的数字表示支持该方法的第一个浏览器的版本号。
ECMAScript5为数组定义了5个迭代方法。每个方法都接收两个参数:要在每一项上运行的函数和(可选的)运行该函数的作用域对象——影响this的值。传入这些方法中的函数会接收三个参数:数组项的值、该项在数组中的位置和数组对象本身。根据使用的方法不同,这个函数执行后的返回值可能会也可能不会影响访问的返回值
function(item,index,array){ //todo }
map()方法对数组的每一项运行给定函数,返回每次函数调用的结果组成的数组
array.map(f,o):f是array的每一个元素调用的函数。它的返回值成为返回数组的元素;o是f调用时的可选this值
map()方法还可以接受第二个参数,表示回调函数执行时this所指向的对象
对于稀疏数组,map()方法不会在实际上不存在元素的序号上调用函数
var arr = [1,,3]; var arr1 = arr.map(function(item,index,arr){return item*2;}); console.log(arr1);//[2, ,6]
map()方法兼容写法
if (typeof Array.prototype.map != "function") { Array.prototype.map = function (fn, context) { var arr = []; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { arr.push(fn.call(context, this[k], k, this)); } } return arr; }; }
forEach()方法对数组中的每一项运行给定函数,这个方法没有返回值。本质上与for循环迭代数组一样。如果需要有返回值,一般使用map方法
forEach()方法除了接受一个必须的回调函数参数,第二个参数还可以接受一个可选的上下文参数(改变回调函数里面的this指向);
第二个参数对于多层this非常有用,因为多层this通常指向是不一致的,可以使用forEach()方法的第二个参数固定this;
forEach()循环可以用于类数组对象
var str = 'abc'; Array.prototype.forEach.call(str, function(item, index, array) { console.log( item + ':' + index); }); //a:0 //b:1 //c:2
与for循环不同,对于稀疏数组,forEach()方法不会在实际上不存在元素的序号上调用函数
var arr = [1,,3]; arr.forEach(function(item,index,arr){arr[index] *= 2}); console.log(arr);//[2, ,6]
var arr = [1,,3]; for (var i =0; i<arr.length; i++) {arr[i] *= 2;} console.log(arr);//[2, NaN, 6]
forEach()方法无法在所有元素都传递给调用的函数之前终止遍历。也就是说,没有像for循环中使用的相应的break语句。如果要提前终止,必须把forEach()方法放在一个try块中,并能抛出一个异常
var arr = [1,2,3,4,5]; arr.forEach(function(item,index,arr){ try{ if(item === 2) throw new Error; }catch(e){ console.log(item); } });
forEach()方法兼容写法
if(typeof Array.prototype.forEach != 'function'){ Array.prototype.forEach = function(fn,context){ for(var k = 0,length = this.length; k < length; k++){ if(typeof fn === 'function' && Object.prototype.hasOwnProperty.call(this,k)){ fn.call(context,this[k],k,this); } } } }
filter()方法对数组中的每一项运行给定函数,该函数会返回true的项组成的数组。该方法常用于查询符合条件的所有数组项
filter()方法还可以接受第二个参数,指定测试函数所在的上下文对象(this对象)
filter()会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的,所以可以压缩稀疏数组的空缺
var arr = [1,2,,,,3,,,,4]; console.log(arr.length);//10 var dense = arr.filter(function(){return true;}) console.log(dense,dense.length);//[1,2,3,4] 4
filter()方法兼容写法
if (typeof Array.prototype.filter != "function") { Array.prototype.filter = function (fn, context) { var arr = []; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { fn.call(context, this[k], k, this) && arr.push(this[k]); } } return arr; }; }
some()方法对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true。并且当且仅当数值中的所有元素调用判定函数都返回false,它才返回false
在空数组上调用every()方法会返回false
some()方法兼容写法
if (typeof Array.prototype.some != "function") { Array.prototype.some = function (fn, context) { var passed = false; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { if (passed === true) break; passed = !!fn.call(context, this[k], k, this); } } return passed; }; }
every()方法对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true;只要有一项返回false,则返回false
在空数组上调用every()方法会返回true
every()方法兼容写法
if (typeof Array.prototype.every != "function") { Array.prototype.every = function (fn, context) { var passed = true; if (typeof fn === "function") { for (var k = 0, length = this.length; k < length; k++) { if (passed === false) break; passed = !!fn.call(context, this[k], k, this); } } return passed; }; }
总结
javascript数组方法特意定义为通用的,因此它们不仅应用在真正的数组而且在类数组对象上都能正确工作。这22种方法中,除了toString()和toLocaleString()以外的所有方法都是通用的
可以改变原数组的方法总共有7种:包括unshift()、shift()、push()、pop()这4种栈和队列方法,reverse()和sort()这2种数组排列方法,数组删改方法splice()
除了上面22种方法外,还有一些方法也是常用的,但是需要自己封装。