温故而知新:数组备忘录

192 阅读16分钟

前言

最近在重新学习Javascrpt,翻看了《javascript高级设计》以及掘金上面很多大佬的博客,顺便把自己学到的东西记录下来,方便新人学习以及自己以后可以复习。希望大家看完以后能有所收获!

JS 并没有真正的数组

javascript的数组并不是真正的数组。像C、C++里面的典型的数组,它们储存的数据类型要求都是相同的,而且物理储存的位置也是连续的,能够通过数字下标来获取元素。但是在javascript中,数组的类型却可以不一样,如果数组内部有对象类型,那么对象是随机储存的,这就导致了它们储存的内存并不连续。而且javascript中并不能通过数字下标来获取元素,而是通过字符串的形式来获取元素,所以我们学习JS的时候,就经常说数组是对象类型的。

有人会问js怎么不可以通过数字下标来获取元素呢。我们来看看下面的代码。

let arr1=[1,2,3,4,5,6]
Object.keys(a)   //返回的结果是['0','1','2','3','4','5']

它的所谓下标其实是字符串类型,那么为什么我一直都是使用数字下标来获取数组中的元素的呢,其实JS他通过隐式转换,将数字类型转换为字符串类型了。

let arr1=[1,2,3,4,5,6]
arr1[1]===arr1[(1).toString()]===arr1['1']  //结果返回的是2

既然数组的所谓的下标是字符串类型,那是不是我可以将它换成其他类型呢,答案是可以的。毕竟我们学JS的时候,一直都说数组是Object类型嘛,我们来试试对象类型的keyvalue的形式来定义一下数组的。

var arr1=[1,2,3,4,5,6]
arr1.jj=7  //返回的结果是 [1,2,3,4,5,6,jj:7]   看,是不是很眼熟,跟{key:value}很像。

一 创建一个数组

let arr=[1,2,3]   //对象字面量
let arr=new Array(1,2,3)   //构造器
let arr=new Array(3)      //构造器,返回的结果是[empty x 10]

(1) Array.of()返回由所有参数组成的数组,不考虑参数的数量或类型,如果没有参数就返回一个空数组 。

let arr=Array.of(1,2,3)   //arr=[1,2,3]
let arr1=Array.of(1)   //arr1=[1]

Array(3)Array.of(3)的区别。Array.of(3)创建一个具有单个元素 3 的数组,而 Array(3)创建一个长度为3的空数组。

(2) Array.from()从一个类数组或可迭代对象中创建一个新的数组。

let p= {0: 'h', 1: 'p', 2:'c', length: 3};
let arr1 = Array.from(p); // ['h','p','c'];
let arr2 = Array.from('what'); // ['w','h','a','t']
let arr = Array.from(new Set(['a','b'])); // ['a','b']
function fn(){
    return Array.from(arguments);
}
fn(1,2,3)  //[1,2,3] 

(3) 通过转化创建数组

let arr1='1,2,3'.split(',')    //arr1=[1,2,3]
let arr2='123'.split('')     //arr2=[1,2,3]

二 数组的方法

1.不改变原数组的方法

(1)slice()方法返回一个从开始到结束(不包括结束)的数组到新数组对象上面

语法:

arr.slice([begin[, end]])

参数:

begin(可选): 接受负值,从该索引处开始提取原数组中的元素,默认值为0。

end(可选):接受负值,在该索引处前结束提取原数组元素,默认值为数组末尾(包括最后一个元素)。

如果省略begin,则从索引为0的开始。提取的原则是包含begin,不包含end。

--简单类型--
let arr = ['a','b','c','d','e','f'];
let arr1 = arr.slice(1,3); // arr1=['b','c'];let arr2 = arr.slice(-2,-1);  // arr1=['e']

--引用类型--
let phone={color:'red',size:'small',cost:100}
let p={phone,1,"hello","world"}
let newphone=p.slice(0,2)
newphone[0].color='gray'
console.log(phone.color)  // gray

也就是说在 p={phone,1,"hello","world"} 中,phone保存的是指向对象phone的指针,而对象类型在堆栈中储存的位置是同一个,所以通过这种方法修改,能够修改对象。

(2) join() 方法将数组(或一个类数组对象)中所有元素都转化为字符串并连接在一起,返回最后生成的字符串

语法:

arr.join(separator)

参数:

separator (可选):指定一个字符串来分隔数组的每个元素。如果没有填入参数,则默认使用逗号分隔。如果是空字符串,则元素之间没有字符。

let color=['yellow','red','gray']
let str1=color.join();   //'yellow,red,gray'
let str2=color.join(',');    //'yellow, red, gray'
let str3=color.join('');    //'yellowredgray'
let obj=[{name:'luffy',age:19},'cap']
let str4=obj.join();   //[object object],test

let arr=[[1,2],3]
let str5=arr.join('+');    //1,2 +3 

(3) toString() 返回一个字符串,表示指定的数组及其元素

语法:

arr.toString()

注意事项:

toString()join()的方法一样,都是用于将数组转化为字符串,但是toString()不能自定义的指定分隔符,默认使用的是逗号分隔。

let arr1=[1,2,'hello','world']
arr.toString();   //1,2,hello,world
[{b:0},'hello'].toString();   //[Object Object],hello

let arr2 = [1, [2, 3], [4, 5], 6];
arr.toString()   //1,2,3,4,5,6

(4) toLocaleString() 返回一个表示数组元素的字符串。该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成

语法:

arr.toLocaleString()

let arr=[{age:13},1,'hello']
let str1=arr.tolocaleString()   //[Object,Object],1,hello

(5) concat() 用于合并两个或多个数组

语法 :

arr.concat([arr1,[arr2,...arrn]])

参数:

arr1...可以是具体的值,或者是数组。当参数为空时,表示数组的浅拷贝;

let arr=[1,2,3]
let arr1=[4,5,6]
let newarr1=arr.concat(arr1);   //newarr=[1,2,3,4,5,6]

let newarr2=arr.concat('hello','nice');    //newarr2=[1,2,3,'hello','nice']

let obj={a:1}
let arr3=['b',obj]
let arr4=[1,2,3].concat(arr3);  // [1,2,3,'b',{a:1}]

arr4.[4].a=hi;    //[1,2,3,'b',{a:'hi'}]

注意事项:

concat()不会改变this或任何作为参数提供的数组,而是返回一个浅拷贝。

concat将对象引用复制到新数组中。 原始数组和新数组都引用相同的对象。 如果引用的对象被修改,那么原始对象也会被修改。

(6)indexOf() 查找数组是否存在某个元素,返回查找到的一个个原始的下标,如果不存在则返回-1

语法:

arr.indexOf(searchElement,fromIndex)

参数:

searchElement(必填):被查找的元素

fromIndex(可选):开始查找的位置,如果查找位置大于等于索引值,返回-1。接受负值,表示从最后一个开始,默认值为0

let arr=[1,2,3,4,5,6];
arr.indexOf(2);    //2
arr.indexOf(7);    //-1
arr.indexOf(3,3);   //-1
arr.indexOf(2,-1);     // -1    从后往前找,如果要查找的数位于起始值的前面,也是返回-1;
arr.indexOf(4,-5);     //3  

let arr1=[1,2,NaN]
arr1.indexOf(NaN);    //-1   无法辨别NaN; 

(7) lastIndexOf() 查找的方向与indexOf相反,而且是返回指定元素在数组最后一个索引

语法:

arr.lastIndexOf(searchElement,fromIndex)

参数:

searchElement (必选): 要查找的元素;

fromIndex(可选): 逆向查找开始位置,默认值数组的长度-1,即查找整个数组。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。当值为负值,且绝对值大于数组长度,则不会被查找;

let arr=[1,2,3,4,5,6,5,4,3,2,1]
let index1=arr.lastIndexOf(2);   //9
let index2=arr.lastIndexOf(5,3);   //-1
let index3=arr.lastIndexOf(5,6);    //6

(8) includes() 方法用来判断一个数组是否包含一个指定的值

语法:

arr.includes(searchElement,fromIndex=0)

参数:

searchElement(必须):被查找的元素;

fromIndex(可选):默认值为0,参数表示搜索的起始位置,接受负值。正值超过数组长度,数组不会被搜索,返回false。负值绝对值超过长数组度,重置从0开始搜索。

let arr=['ok,'why',know',NaN]
let a=arr.includes('why',100);   // false 超过数组长度 不搜索
lat b=arr.includes('why',-3);    //true 从倒数第三个元素开始搜
let c=arr.includes('why',-100);  //true  负值绝对值超过数组长度,搜索整个数组

2.改变原始数组的方法

(1)splice()在原数组的基础上添加或者删除数组元素

语法:

arr.splice(start,deleteCount,item1,.....,itemX)

参数:

start指定修改的开始位置(从0开始计数),如果超过数组长度,则是在数组末尾添加元素,如果是负数,则从末尾开始计数(末位为-1)

deleteCount表示要移除数组元素的个数,如果为零,则不移除。这种情况下是添加元素,如果 deleteCount大于start 之后的元素的总数,则从 start 后面的元素都将被删除(含第start 位)

item i指定要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。

let arr1=[1,2,3,4,5,6,7,8,9]    //以下操作都不建立在前一次的操作之上的;

let a=arr1.splice(0,3);   //a=>[1,2,3]   arr1=>[4,5,6,7,8,9]   从第一个元素开始,删除三个元素;
let b=arr1.splice(-1,4);   //b=>[9]    arr1=>[1,2,3,4,5,6,7,8]  从最后一个元素开始,删除四个元素,但是最后一个只有一个元素,所以只删除一个元素
let c=arr1.splice(2,0,'hi');   //c=>[]   arr1=>[1,2,'hi',3,4,5,6,7,8,9]    从第三个元素开始删除零个,并在该位置插入字符串,其他的元素后移
let d=arr1.splice(3,4,'what');    //d=>[4,5,6,7]   arr1=>[1,2,3,'what',8,9]    从第四个元素开始,删除四个元素,并插入一个字符串
let e=arr1.splice(3);    //e=>[4,5,6,7,8,9]  arr1=>[1,2,3]     从第四个元素开始删除,只剩下前三个
let f=arr1.splice(-4,2,'uu');   f=>[6,7]   arr1=>[1,2,3,4,5,'uu'8,9]    从倒数第四位开始,删除2个数,并插入一个字符串

(2)sort()将数组中的元素排序并返回排序后的数组

语法:

arr.sort([compareFunction])

参数:

compareFunction用来指定按照某种顺序进行排列,如果省略,元素按照转换为字符串的各字符串的Unicode的位点来进行排列。如果不省略,则数组会按照调用该函数的返回值进行排序。

如果比较函数返回值<0,那么a将排到b的前面;

如果比较函数返回值=0,那么ab相对位置不变;

如比较函数返回值>0,那么b 排在a将的前面;

let arr1=[1,4,3,2,7]
arr1.sort((a,b)=>{
if(a>b){
 return 1;}else if(a===b){
    return 0;
}else{
     return -1}
)

console.log(arr1)    //[1,2,3,4,7]

--数组的元素是对象--
let arr2=[{id:2,name:'lisa'},{id:1,name:'homey'},{id:3,name:'barz'}]
arr2.sort((a,b)=>{
  return a.id-b.id; 
})   //上面的判断也可以直接写成该形式;
    //{id:1,name:'homey'},{id:2,name:'lisa'},{id:3,name:'barz'}

(3) push()将一个或多个元素添加到数组的末尾,并返回该数组的新长度

语法:

arr.push(element1, ..., elementN)

参数:

element i表示要添加到数组末尾的元素

let arr=[1,2,3]    //以下操作都不建立在前一次的操作之上的;
let item1=arr.push([4,5,6])  //item1=4  arr=[1,2,3,[4,5,6]]
let item2=arr.push({name:'hi'})   // item2=4   arr=[1,2,3,{name:'hi'}]
let item3=arr.push(6,7,8,'ok')    //item3=7   arr=[1,2,3,6,7,8,'ok']

(4) pop() 删除数组的最后一个元素,减小数组长度并返回它删除的值。

语法:

arr.pop()

无参数

let arr=[1,2,3,'fine','purified',{name:'judy'}]   
let item1=arr.pop()   // item1={name:'judy'}    arr=[1,2,3,'fine','purified']

(5)shift()从数组中删除第一个元素,并返回该元素的值。

语法:

arr.shift()

无参数

let arr=[1,2,3,'angel','vertify',[1,2,3]]
let item=arr.shift()   // 1   arr=[2,3,'angel','vertify',[1,2,3]]

(6) unshift()将一个或多个元素添加到数组的开头,并返回该数组的新长度。

语法:

arr.unshift(element1, ..., elementN)

参数:

element i表示要插入到数组前面的元素

let arr=[1,2,3]      //以下操作都不建立在前一次的操作之上的;
let item1=arr.unshift(6,7,8)    //item1=6  arr=[6,7,8,1,2,3]
let item2=arr.unshift(['a','b','c'])  //item2=4  arr=[['a','b','c'],1,2,3]

(7) reverse() 将数组中的元素颠倒顺序,返回逆序的数组。

语法:

arr.reverse()

无参数

let a=[1,2,3] 
let item=a.reverse()   //item=[3,2,1]    a=[3,2,1]

(8) copyWithin()浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。

语法:

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

参数:

target表示从该位置开始替换数据,如果是负值,表示从后往前计;

start表示开始复制元素的开始位置,如果是负数,表示从后往前计;

end表示开始负值元素的结束位置,而且不包含end的元素,如果是负数,则表示从后往前计,如果忽略,则会复制到末尾;

let arr=[1,2,3]     //以下操作都不建立在前一次的操作之上的;
let item1=arr.copyWithin(0, -2, -1)    //item1=[2,2,3]   arr=[2,2,3]
let item2=arr.copyWithin(0, 1)    //item2=[2,3,3]    arr=[2,3,3]

(9) fill() 用一个固定值填充一个数组中从起始索引到终止索引内的全部元素,且数组长度不会改变。

语法:

arr.fill(value[, start[, end]] )

参数:

value用来填充数组元素的值。

start表示起始索引,默认值为0

end终止索引,默认值为 this.length

let arr=[1,2,3,4,5]     //以下操作都不建立在前一次的操作之上的;
let item1=arr.fill(6)   //item1=[6,6,6,6,6]    arr=[6,6,6,6,6]
let item2=arr.fill(8,1,3)    //item2=[1,8,8,4,5]    arr=[1,8,8,4,5]

3 遍历方法

(1)forEach() 按升序为数组中含有效值的每一项执行一次回调函数。

语法:

arr.forEach(callback(currentValue, index, arr), thisArg)

参数:

currentValue表示数组中正在处理的当前元素。

index表示数组中正在处理的当前元素的索引。

arr表示正在被处理的数组。

thisArg默认指向undefined,当使用箭头函数时,该参数被忽略;

注意事项:

forEach()无法中途退出循环,只能使用return 退出本次回调,进行下一次回调;该方法总是返回undefined,即使指定让他return一个值也没用;

空元素不会遍历,但undefinedunll会遍历;

在迭代过程中删除的元素,或者空元素会跳过回调函数;

遍历次数在一开始就已经确定了,再迭代过程中添加到数组的元素不会被遍历;

如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。

let arr1 = ['a','b', ,null,undefined,'c']; 
arr1.forEach((value,index,arr)=>{
    console.log(value,index,arr)
})    //'a' 0 ['a','b',empty,null,undefined,'c']    'b' 1 ['a','b',empty,null,undefined,'c']  null 3 ['a','b',empty,null,undefined,'c']  undefined 4 ['a','b',empty,null,undefined,'c']    'c' 5 ['a','b',empty,null,undefined,'c']
-----------
let arr2=[1,2,3]
arr2.forEach((value,index,arr)=>{
if(index===0){
   delete arr2[2];
}
console.log(value,index,arr)
})   //1 0 (3) [1, 2, empty]     2 1 (3) [1, 2, empty]
----------
let arr3 = ['a','b','c','d'];
arr3.forEach((value,index,arr)=> {
  if(index === 1) {
    arr3.shift() //遍历到第二项的时候,删除第一项
  }
  console.log(value,index,arr)
})
// 打印信息如下,遍历到第二项的时候,删除第一项,会跳过第三项
// a 0 (4) ['a','b','c','d']     b 1 (3)  ['a','b','d']     d 2 (3)  ['a','b','d']

(2) map()创建一个新数组,其结果是该数组中的每个元素都调用一个callback函数后返回的结果。

语法:

arr.map(function(currentValue, index, arr), thisArg)

参数:

同foreach()

注意事项:

map()不修改被调用的数组。它具有与原数组相同的长度。

let arr=[1,3,5,7,9]
let newarr=arr.map((item,index)=>{       //newarr=[1,6,10,14,18]
     return item*2;})         //如果不使用return  那么返回的新对象则是undefined;

(3)filter()返回一个新数组, 其包含通过所提供函数实现的测试的所有元素。

语法:

arr.filter(function(currentValue, index, arr), thisArg)

参数:

同foreach()

let arr=[12,4,47,55,19,38]
let newarr=arr.filter((item,index)=>{
    return item>30;
})         //newarr=[47,55,58]

(4)some()判断数组中的元素是否有元素符合判断条件,当且仅当所有元素都不符合条件时,才返回false;

语法:

arr.some(function(currentValue, index, arr), thisArg)

参数:

同foreach()

let arr=[1,2,3,4,5,6,7,8]
arr.some(item=>{
   return item%2===1})    //true

当然也可以封装成为一个函数
function isodd(ele,index){
    return ele%2===1}
let result =arr.some(isodd);     //true 

(5) every()判断数组中的元素是否都符合判断条件,当且仅当所有元素都符合条件时,才返回true;

语法:

arr.every(function(currentValue, index, arr), thisValue)

参数:

同foreach()

let arr=[1,2,3,4,5,6,7,8]
arr.every(item=>{
   return item%2===1})    //false

当然也可以封装成为一个函数
function isodd(ele,index){
    return ele%2===1}
let result =arr.every(isodd);     //false

(6)reduce()使用指定的函数将数组元素进行组合,生成单个值。reduceRight()则从右往左进行相同的操作。

语法:

arr.reduce(function(total, currentValue, currentIndex, arr), initialValue)

参数:

total为累加器

currentValue数组当前元素的值

index 当前元素的索引值

initialValue 指定第一次回调 的第一个参数。

注意事项:

如果 initialValue 在调用 reduce 时被提供,那么第一个 total 将等于 initialValue,此时 currentValue 等于数组中的第一个值;

如果 initialValue 未被提供,那么total 等于数组中的第一个值,currentValue 等于数组中的第二个值。此时如果数组为空,那么将抛出TypeError

如果数组仅有一个元素,并且没有提供initialValue,或提供了 initialValue 但数组为空,那么回调不会被执行,数组的唯一值将被返回;

----当initialValue为0时----
let arr=[1,2,3,4]
let sum1=arr.reduce((total,currentvalue)=>total+currentvalue,0);
console.log(sum1)     //10


----当initialValue不为0时-----
let arr=[1,2,3,4]
let sum2=arr.reduce((total,currentvalue)=>total+currentvalue,5);
console.log(sum2)     //15

---使用reduce模拟map---
let arr=[1,2,3,4,5]
let result1=arr.reduce((result,item)=>{return rsult.concat(item*item)},[])
console.log(result1)    //[1,4,9,16,25]


---使用reduce模拟filter---
let arr=[1,2,3,4,5]
let result2=arr.reduce((result,item)=>{
if(item%2===1){
    return result
}else{
    return result.concat(item)
}},[])

console.log(result2)     //[2,4]

(7)find()查找第一个复合条件的元素,并返回该元素,否则返回undefined;findIndex()查找第一个复合条件的元素,并返回该元素的索引,否则返回-1

语法:

let element =arr.find(function(elemnet, index, arr), thisArg)

let index = arr.findIndex(function(elemnet, index, arr), thisArg)

参数:

同foreach()

---find()---
let arr=[1,3,7,9,13,16]
let a=arr.find((item,index)=>item>10)    //a=13
let b=arr.find((item,index)=>item>20)     //b=undefined

---findIndex()---
let c=arr.findIndex((item,index)=>item<-1)     //c=-1
let d=arr.findIndex((item,index)=>item>5)      //d=2

(8)keys() 、values()、entries()返回一个新的数组迭代器,分别是包含数组中每个索引的键或者是值或者是键与值。

语法:

arr.keys()

arr.values()

arr.entries()

无参数

---keys()---
let arr=['a','b','c']
let a=Object.keys(arr)     //a=['0','1','2']

---keys()---
let arr=['a','b','c']
let b=Object.values(arr)     //b=['a','b','c']

---entries()---
let arr=['a','b','c']
let c=Object.values(arr)    //c=[['0','a'],['1','b'],['2','c']]

总结

花了几天的零碎时间整理了至ES6的几乎所有的数组的操作方法,在参考了许多大佬的博客以及官方文档后,对数组的许多API的理解也更加深入了。文章如有不正确的地方欢迎各位大佬鞭策!

希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。

参考链接

MDN Array

详解JS遍历

javascript Array数组相关汇总

【深度长文】JavaScript数组所有API全解密