数组在工作中使用频繁,但我们大多数情况下只使用到了部分特性,久而久之,对于数组其它的相关特性就变得模糊起来,不免有错漏不全的理解,这里花了一点时间,对最常用的数组做一些总结。
定义
数组的标准定义:一个存储元素的线性集合,元素可以通过索引来任意存取,索引通常是数字,用来计算元素之间存储位置的偏移量。
数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。
内置方法
- concat / slice / splice
- sort / reverse
- shift/ unshift
- pop / push
- toString
- join
从已有的数组创建新的数组
concat / slice / splice
concat 合并两个或多个数组,返回合并结果
不改变原有数组,返回一个新的合并数组
arrayObject.concat(arrayX,arrayX,......,arrayX),
arrayX可以是值/数组对象
[1,2].concat(3,4); //[1, 2, 3, 4]
[1,2].concat([3],[4,5],[6]); //[1, 2, 3, 4, 5, 6]
//添加一个元素到头部
[1].concat([2,3]);//[1, 2, 3]
//添加一个元素到尾部
[1,2].concat(3);//[1, 2, 3]
slice 从现有的数组返回选定的元素组成的数组 不改变原有数组
arrayObject.slice(start,end) start 和 end 定义了选取范围,正数从0开始,负数从-1开始,始终为左闭右开,也就是左边包含右边不包含
[1,2,3,4].slice(0,1);//[1]
[1,2,3,4].slice(1,3);//[2,3]
[1,2,3,4].slice(-1);//[4]
[1,2,3,4].slice(-2);//[3,4]
[1,2,3,4].slice(-2,-1);//[3]
splice 方法向/从数组中添加/删除项目,然后返回被删除的项目组成的新数组 会改变原有数组
arrayObject.splice(index,howmany,items,...)
index 起始位置,可以是负数
howmany 要删除的个数,0为不删除
items 要添加的元素
var arr1 = [1,2,3,4];
var arr2 = arr1.slice();
var arr3 = arr1.slice();
// 删除元素
var removed = arr1.splice(1,1);
console.log(removed);//[2]
console.log(arr1); //[1, 3, 4]
//删除了索引为1的元素2,并返回这个元素,原数组发生改变
// 替换元素
var replace = arr2.splice(2,1,5);
console.log(replace);//[3]
console.log(arr2);//[1, 2, 5, 4]
// 在索引为2的位置,删除了1个元素3,并在此插入新的元素5,返回了删除的元素,原数组发生变化
// 删除并插入元素
var insert = arr3.splice(3,0,5,6,7);
console.log(insert); //[]
console.log(arr3); //[1, 2, 3, 5, 6, 7, 4]
// 在索引为3的位置,删除0个元素,插入了5、6、7这3个元素到索引为3的前面
数组排序
sort / reverse
sort 用于对数组的元素进行排序
将对原数组操作,会改变原数组
arrayObject.sort(sortFn)
sortFn 为函数,用于提供排序比较规则,返回值是正值,则第一个参数比第二个大,负值,则第1个比第2个小,零,则两个数相等
如果sortFn为空,则按照字符编码的顺序进行排序
[1,51,4,12,2,3].sort() //[1, 12, 2, 3, 4, 51]
[1,51,4,12,2,3].sort(function(a,b){return a-b}) //[1, 2, 3, 4, 12, 51]
[1,51,4,12,2,3].sort(function(a,b){return b-a}) //[51, 12, 4, 3, 2, 1]
//或者
[1,51,4,12,2,3].sort(new Function('a','b','return a-b'))
[1,51,4,12,2,3].sort((a,b)=>a-b)
reverse 用于颠倒数组中元素的顺序 会改变原有的数组
[1,3,4,5].reverse() //[5, 4, 3, 1]
//反转字符串
'54321'.split('').reverse().join(''); //'12345'
//反转数字
Number(Number(12345).toString().split('').reverse().join('')) //54321
为数组增减元素
- push 添加 元素至数组尾部,返回新的数组长度
- unshift 添加 元素至数组开头,返回新的数组长度
- pop 删除 尾部元素,返回删除的元素
- shift 删除 头部元素,返回删除的元素
都是直接对原数组操作,会改变原有数组
var arr = [1,2,3,4];
var arr1 = arr.slice();
var arr2 = arr.slice();
var arr3 = arr.slice();
var arr4 = arr.slice();
console.log(arr1.push(5), arr1); //5 [1, 2, 3, 4, 5]
console.log(arr2.unshift(0), arr2); //5 [0, 1, 2, 3, 4]
console.log(arr3.pop(), arr3); //4 [1, 2, 3]
console.log(arr4.shift(), arr4); //1 [2, 3, 4]
数组转字符串
toString / join
[1,2,3,4].toString() //"1,2,3,4"
[1,2,3,4]+'' //"1,2,3,4"
[1,2,3,4].join('-');//"1-2-3-4"
ES5新增
- Array.isArray()
- [].indexOf() / [].lastIndexOf()
- [].forEach / [].map()
- [].every() / [].some()
- [].filter()
- [].reduce() / [].reduceRight()
数组类型判断
自定义一个方法来判断是否为数组
function isArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}
isArray([]); //true
ES5新增 Array.isArray() 来判断是否为数组
Array.isArray([]);//true
Array.isArray({a:1});//false
// IE9以下兼容写法
if(typeof Array.isArray !== 'function'){
Array.isArray = function(obj){
return Object.prototype.toString.call(obj) === '[object Array]';
}
}
查找元素
indexOf(el, startIndex) / lastIndexOf(el, lastIndex)
没有查找到元素返回-1,查找到元素,返回查找到的第一个的索引值,lastIndexOf 为从后面查起
- startIndex 表示从哪个索引开始找,默认为第1个索引
- lastIndex 表示从倒数第几个索引开始找,默认为最后一个索引
[1,2,3,2,4].indexOf(2); //1
[1,2,3,2,4].indexOf(2,2); //3
[1,2,3,2,4].lastIndexOf(2); //3
[1,2,3,2,4].indexOf(5); //-1
[1,'1'].indexOf('1');//1
// 下面的查找返回了-1,=== 运算符的缺陷,后面ES6的includes方法会弥补这一缺陷
[1,NaN].indexOf(NaN);//-1
[1,{}].indexOf({});//-1
// IE9以下兼容写法
if(Array.prototype.indexOf !== 'function'){
Array.prototype.indexOf = function(searchEl, startIndex){
var index = -1;
startIndex = startIndex * 1 || 0;
for (var k = 0, len = this.length; k < len; k++) {
if (k >= startIndex && this[k] === searchEl) {
index = k;
break;
}
}
return index;
}
}
if(Array.prototype.lastIndexOf !== 'function'){
Array.prototype.lastIndexOf = function(searchEl, lastIndex){
var index = -1, len = this.length;
lastIndex = lastIndex * 1 || len;
while (len--) {
if (len <= lastIndex && this[len] === searchEl) {
index = len;
break;
}
}
return index;
}
}
数据遍历
[].forEach() / [].map()
[].forEach(function(value, index, arr){}, context)
循环遍历数组中的每一个元素,处理函数中的参数依次为:当前元素、当前元素索引、原数组
forEach的第二个参数,可以指定处理函数中this的指向
var arr = [1,2,3];
arr.forEach(function(v,i,a){
console.log(v,i,a);
v+=1;
})
console.log(arr); //[1,2,3]
// 1 0 [1, 2, 3]
// 2 1 [1, 2, 3]
// 3 2 [1, 2, 3]
// 等同于
for(var i=0; i<arr.length; i++){
console.log(arr[i], i, arr);
}
// IE9以下的兼容性写法
if(Array.prototype.forEach !== 'function'){
Array.prototype.forEach = function(fn, context){
for(var i=0, len=this.length; i<len; i++){
if(typeof fn === 'function' && Object.prototype.hasOwnProperty.call(this, i)){
fn.call(context, this[i], i, this);
}
}
}
}
jQuery中的 $.each(function(index, values, arr){}) 回调中的参数第1个是索引,第2个是元素,$.map() 亦是如此
map(function(value, index, arr){}) 与forEach类似,返回一个新的数组,不改变原有数组
var arr = [1,2,3];
var res = arr.map(function(v){
return v+1;
});
console.log(arr); //[2, 3, 4]
console.log(res); //[1, 2, 3]
// IE9以下的兼容性写法
if(Array.prototype.map !== 'function'){
Array.prototype.map = function(fn, context){
var res = [];
if(typeof fn === 'function'){
for(var i=0, len=this.length; i<len; i++){
res.push(fn.call(context, this[i], i, this));
}
}
return res;
}
}
map的回调处理函数一定要有返回值,如果没用,则会返回undefined
检测是否包含某元素
[].every(function(value, index, arr){}, context) / [].some(function(value, index, arr){}, context)
- 数组元素的逻辑判定,回调函数返回true/false
- some是所判定的元素只要数组中有一个符合条件就返回true
- ervery是数组中必须所有元素都符合条件就返回true,否则返回false
- 在空数组上调用every返回true,some返回false
console.log([1,2,3,4].some(v=>v>3)) //true
console.log([1,2,3,4].every(v=>v>3)) //false
console.log([].every(v=>v>3)) //true
console.log([].some(v=>v>3)) //false
// IE9以下兼容写法
if(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;
}
}
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;
};
}
数组过滤
[].filter(fn, context)
- 当遍历元素时,fn返回true,则返回这个元素,否则不返回,fn中返回值只要弱等于true或false即可,使用==比较
- filter返回一个新的结果数组,不改变原有数组
[1,2,3,4].filter(v=>v>2); //[3, 4]
[1,2,0,null,''].filter(v=>v); //[1, 2]
// IE9以下兼容写法
if(Array.prototype.some !== 'function'){
Array.prototype.some = 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;
}
}
数组聚合
[].reduce(fn, initialValue) / [].reduceRight(fn, initialValue)
- 将数组元素聚合(合并)为一个元素
- fn 的参数依次为 (之前值、当前值、索引值、数组本身),
- initialValue 表示最初的值,如果不存在,初始值为数组第1个元素
reduceRight 与 reduce 用法一致,区别是从最后一个元素开始迭代
ar res = [1,2,3,4].reduce(function(prev, curr, index, arr){
console.log(prev, curr, index, arr);
return prev+=curr;
})
console.log(res);//10
// 没有initialValue,则初始值为1
// 1 2 1 [1, 2, 3, 4]
// 3 3 2 [1, 2, 3, 4]
// 6 4 3 [1, 2, 3, 4]
[1,2,3,4].reduce(function(prev, curr, index, arr){
console.log(prev, curr, index, arr);
return prev+=curr;
}, 10)
// 如果initialValue有值,则初始值为initialValue的值
// 10 1 0 [1, 2, 3, 4]
// 11 2 1 [1, 2, 3, 4]
// 13 3 2 [1, 2, 3, 4]
// 16 4 3 [1, 2, 3, 4]
[1,2,3,4].reduceRight(function(prev, curr, index, arr){
console.log(prev, curr, index, arr);
return prev+=curr;
})
// 从最后一个元素开始迭代,初始值为4
// 4 3 2 [1, 2, 3, 4]
// 7 2 1 [1, 2, 3, 4]
// 9 1 0 [1, 2, 3, 4]
// 二维数组扁平化
[[1,2],[3,4]].reduce((prev,curr)=>prev.concat(curr)) //[1, 2, 3, 4]
// IE9以下兼容写法
if (typeof Array.prototype.reduce != "function") {
Array.prototype.reduce = function (fn, initialValue ) {
var prev = initialValue, k = 0, len = this.length;
if (typeof initialValue === "undefined") {
prev = this[0];
k = 1;
}
if (typeof fn === "function") {
for (k; k < len; k++) {
this.hasOwnProperty(k) && (prev = callback(prev, this[k], k, this));
}
}
return previous;
};
}
if (typeof Array.prototype.reduceRight != "function") {
Array.prototype.reduceRight = function (fn, initialValue ) {
var len = this.length, k = len - 1, prev = initialValue;
if (typeof initialValue === "undefined") {
prev = this[len - 1];
k--;
}
if (typeof fn === "function") {
for (k; k > -1; k--) {
this.hasOwnProperty(k) && (prev = callback(prev, this[k], k, this));
}
}
return previous;
};
}
ES6新增
Array.from(arrLike) //类数组转数组
Array.of(…args) //将一组值转成数组
[].copyWithin(target, start, end) //复制数组成员到指定位置,会覆盖原有成员
[].find(fn) // 查找元素,返回元素本身
[].findIndex(fn) // 查找元素返回元素索引,没有则返回-1
[].includes() // 检测数组是否包含某元素,返回true/false
[].fill(value, start, end) //填充数组
[].keys() // 返回数组的key组成的数组
[].values() // 返回数组的值组成的数组
[].entries() // 返回数组索引和值组成的数组
数组转换
Array.from(arrLike) / Array.of(...args)
Array.from 可以将类数组(有length属性的Object/可遍历(iterable)的对象/Set对象/Map对象)转换为数组
let arrayLike = {'0':'a', '1':'b', '2':'c', length:3};
// 以前的写法
[].slice.call(arrayLike)//["a", "b", "c"]
// 使用Array.from
let arr1 = Array.from(arrayLike); //["a", "b", "c"]
let set = new Set([1,3,4,5]);
let arr2 = Array.from(set);//[1,3,4,5]
let map = new Map([[1,'a'], [2,'b'], [3,'c']]);
let arr3 = Array.from(map);//[[1,'a'],[2,'b'],[3,'c']]
let arr = [1,2,3,4];
let iter = arr[Symbol.iterator]();
iter.next();//{value: 1, done: false}
let arr4 = Array.from(iter); //[2,3,4]
Array.of 用于将一组值转换为数组
使用 Array 的构造函数初始化数组时,如果参数是1个,那么会创建这个参数长度的数组,数组的每一项为 undefined,如果参数是多个,那么会创建这多个参数组成的数组,所以参数的个数会导致结果的不一致,Array.of 正是弥补这一点的扩展
Array(2);//[,]
Array(2,3);//[2,3]
Array.of(2);//[2]
Array.of(2,3);//[2,3]
未完待续。。。