1

数组在javascript中使用度非常频繁,我总结了一些在数组中很常见的问题。

关于数组中的方法非常多,我总结了一张表来大致了解数组中的方法

Array中的方法 含义 改变原数组 返回值 ES6新增
concat 合并两个或多个数组 false 新数组 false
copyWithin 浅复制数组的一部分到同一数组中的另一个位置 true 改变后的数组 true
entries 返回数组迭代器对象,该对象包含数组中每个索引的键/值对 false 数组迭代器 true
every 测试数组的所有元素是否都通过了指定函数的测试 false 布尔值,true/false false
fill 用一个固定值填充一个数组中从起始索引到终止索引内的全部元素 true 改变后的数组 true
filter 创建一个新数组, 其包含通过所提供函数实现的测试的所有元素 false 新数组 false
find 返回数组中满足提供的测试函数的第一个元素的值。否则返回undefined false javascript语言类型 true
findIndex 返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1 false 数组索引 true
forEach 遍历数组 false undefined false
includes 判断一个数组是否包含一个指定的值 false 布尔值,true/false true
indexOf 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1 false 数组索引 false
join 将数组(或一个类数组对象)的所有元素连接到一个字符串中 false 字符串 false
keys Array迭代器,它包含数组中每个索引的键 false 数组迭代器 true
lastIndexOf 返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1 false 数组索引 false
map 遍历数组 false 新数组 false
pop 从数组中删除最后一个元素,并返回该元素的值 true 数组元素 false
push 将一个或多个元素添加到数组的末尾,并返回新数组的长度 true 数组长度 false
reduce 对累加器和数组中的每个元素(从左到右)应用一个函数,将其减少为单个值 false 函数返回值 false
reduceRight reduce执行方向相反,从右到左 false 函数返回值 false
reverse 将数组中元素的位置颠倒 true 改变后的数组 false
shift 从数组中删除第一个元素,并返回该元素的值 true 数组元素 false
slice 可从已有的数组中返回选定的元素 false 新数组 false
some 测试数组中的某些元素是否通过由提供的函数实现的测试 false 布尔值,true/false false
sort 在适当的位置对数组的元素进行排序 true 一个新数组 false
splice 删除现有元素和/或添加新元素来更改一个数组的内容 true 删除的元素数组 false
toLocaleString 返回一个字符串表示数组中的元素 false 字符串 false
toString 返回一个字符串,表示指定的数组及其元素 false 字符串 false
unshift 将一个或多个元素添加到数组的开头 true 数组长度 false
values 一个数组迭代器对象,该对象包含数组每个索引的值 false 数组迭代器 true

从这个表中我们要小心几个方法,reverse和sort会改变原数组,并返回改变的新数组,push和unshift方法返回的是数组长度而不是数组,forEach方法返回的是undefined不是数组。

此外,我还需提一下slice和splice这两个方法,说实话这两个方法看起来很像,容易让人搞混,最关键的是用到的频率还蛮高的,这两个方法就像字符串中substr和substring这两个老兄弟,闲着没事就喜欢去迷惑别人,本人就曾深深的被这两个方法伤害过。

slice接受两个参数start和end,代表需要截取的数组的开始序号和结束序号。

var arr = [4,3,5,8,9,6];
arr.slice(0)    // [4,3,5,8,9,6],end可以省略,默认为数组长度
arr.slice(0,4)   //[4,3,5,8]
arr.slice(-1);   //[6],  start为负数代表从数组截取的开始序号从尾部算起
arr.slice(0,-1);  //[4,3,5,8,9]   end为负数表示结束序号从尾部算起
arr.slice(2,0);   //[]
arr.slice(-1,-1);   //[]  如果start和end符号相同,end一定大于start,否则返回的会是[]

splice的参数为index,deleteCount和...items,index表示需要删除或添加原数时的位置,负数表示从尾部算起,deleteCount表示要删除的元素,0表示不删除。其中items表示添加的元素个数。

var arr = [4,3,5,8,9,6];
arr.splice(0,0)      //返回[], arr=[4,3,5,8,9,6];
arr.splice(0,2)      //返回[4,3], arr=[5,8,9,6];
arr.splice(0,2,3,4)  //返回[5,8], arr=[3,4,9,6];

splice不管是添加还是删除元素,返回的都是删除元素的列表,splice是先做删除操作,后添加

var arr = [4,3,5];
arr.splice(3,1,8,9);     //返回[], arr= [4, 3, 5, 8, 9];
//如果index大于数组长度,那么splice不会删除元素

注意:虽然slice和splice都返回一个新的数组,但是slice不会改变原数组,splice会改变原数组,这个区别非常关键。

最后在加一些经常会问到的数组问题。

1.创建数组

//数组字面量创建
var arr = [1,2];

//Array构造器创建;
var arr = Array(1,2);     //[1,2]  可以用new操作符,也可以不用
//Array构造器有个局限性,不能创建只有单个数字的数组
var arr = Array(10)       //创建的是一个长度为10的空数组,并不是[10]
//如果传入的不是Number类型的数字,那么没有任何问题
var arr = Array('10')     //['10']

//此时如果要创建只有单个数字的数组,可以用Array.of方法
var arr = Array.of(10)    //[10]
var arr = Array.of(1,2)   //[1,2]

//Array.from( items [ , mapfn [ , thisArg ] ] )
//items是个可迭代对象,mapfn是遍历该迭代对象的function,thisArg是mapfn中的this对象
var arr = Array.from([1,2,3])    //[1,2,3]

Array.from是非常有用的创建数组方法,能把字符串转化为数组,Map,Set也能转成数组。

Array.from('abc')        //['a','b','c'];
Array.from(new Set([1,2,3]))  //[1,2,3],当然这个例子毫无意义
Array.from(new Map([[1,2],[3,4]]))  //[[1,2],[3,4]]

我们知道用Array构造器创建的数组是个空数组,map,forEach方法并不能遍历这个数组。

var arr = Array(10);
arr.forEach((item,index) => console.log(index))   //不会有输出
//map,forEach循环判断的是该对象上有没有对应的属性
arr.hasOwnProperty(0)            //false,以hasOwnProperty为判断标准
//Array.from中的mapfn方法是以迭代方式来判断的,因此
Array.from(arr,(item,index)=>console.log(index))   //0,1,2,3,4,5,6,7,8,9

由于这个原因,我们可以快速对数组初始化,比如创建一个0到99的数组

Array.from(Array(100),(item,index)=>index);
//当然,如果你用到上表中Array的keys方法那更快捷
Array.from(Array(100).keys());

2.数组去重

方法一,创建对象的键值唯一性来进行去重:

var arr = [1,2,3,1,3,5,3,2];
var _arr = [];
var obj = {};
arr.forEach(item => {
    if(!obj[item]){
       _arr.push(item);
       obj[item] = true;
    }
})
arr = _arr;

方法二,结合Set的键值唯一性以及Array.from方法可以快速数组去重:

var arr = Array.from(new Set([1,2,3,1,3,5,3,2]))   //[1,2,3,5]

3.快速复制一个数组

var arr = [1,2,3,4];
var arr1 = arr.slice();
var arr2 = arr.concat();
注:这里的复制指的是浅拷贝

4.求数组最大值,最小值

这里的数组指的是全是数字的数组

方法一,sort排序后取值

var arr = [1,4,6,2,33,19,6,9];
var maxvalue = arr.sort((a,b) => b>a )[0]
var minvalue = arr.sort((a,b) => a>b )[0]

方法二,Math的max和min方法调用

var arr = [1,4,6,2,33,19,6,9];
var maxvalue = Math.max.apply(null,arr);   //33
var minvalue = Math.min.apply(null,arr);   //1

5.数组排序

在不用系统自带的sort的情况下对数组排序有很多方法,比如冒泡、插入以及快速排序等。但我总觉得这些排序方法还是过于复杂,有没有更快以及更方便的排序,我思考了好久,后来先想到了可以用数组的序号进行排序。原理是把数组1中的值变成数组2中的序号:

var arr = [3,4,6,2,8,7,5],
    arr2 = [];
arr.forEach(item => arr2[item] = item);
arr = [];
arr2.forEach(item => arr.push(item));  

写完之后自己感觉美滋滋,可之后发现如果数组中有负数,不就都玩完了吗。于是赶紧改:

var arr = [3,-4,6,-2,-8,7,5],
    parr = [];
    narr = [];
arr.forEach(item => item>=0?parr[item] = item:narr[-item] = item);
arr = [];
parr.forEach(item => arr.push(item));
narr.forEach(item => arr.unshift(item));    
注:如果数组中有重复数字则排序方法有误,会把重复数字去掉。

写完之后发现其实也没有比冒泡、插入以及快速排序的方法快多少。

6.求一个整数数组是否包含另一个整数数组

一开始我想到一个方法,把两个数组转换成字符串,在进行includes或者indexOf判断就可以了,后来我发现了问题:

var a = [2,4,8,6,12,67,9];
var b = [8,6,12];
a.join(',').includes(b.join(','));   //true;  这是可以的

var b = [8,6,1]
a.join(',').includes(b.join(','));   //true;  这居然也可以,显然有问题

//于是改成
a.join(',').includes(','+b.join(',')+',');  //false;

//后来我又发现如果b数组在a数组的开头和结尾都会有问题,于是又改成如下:

(','+a.join(',')+',').includes(','+b.join(',')+',');  //false;

写这篇文章主要是对自己学习数组做一个总结。如果对上面的问题有更好的解答,欢迎留言告知。


fanqifeng
536 声望24 粉丝

javascript语言爱好者,喜欢发掘js中的特异之处。现阶段沉迷购书无法自拔。