与其他编程语言中的数组不同的是:
js数组是js中的一种特殊的(内置)对象,索引更像是属性名,只是刚好是整数。
在js中,数组是无类型限制的(即数组的元素可以是任意类型,包括自定义对象)。
ja数组是基于零且使用32位数值索引的(从最小0到最大2^32-2)
js数组是动态的
js数组可以是稀疏的(即元素不一定有连续的索引,中间可能有缝隙)
对于稀疏数组,length是大于所有元素的最高索引。
数组从Array.prototype继承属性,这个原型定义了很多数组操作方法。(并且很多都是泛型的)
ES6新增定型数组:
下面将会从如何创建数组,一直到如何使用JS数组进行详细的介绍。(包含代码实例以及对一些易错易混的方法进行辨析),可以收藏以便不时之需。
var arr1 = new Array(); //创建空数组
var arr2 = new Array(20); //创建一个包含20项的数组
var arr3 = new Array(1,2,3) //包含三个数值的数组
var arr4 = []; //创建一个空数组
var arr5 = [20]; // 创建一个包含1项的数组
var arr6 = [1.1,true,"a",]; // 创建一个包含3个不同类型元素的数组
数组字面量中的值不需要是常量,可以是任意表达式
如果数组字面量中连续包含多个逗号,且逗号之间没有值,则这个数组为稀疏数组
let count = [1,,3]; //索引0和2有元素,索引1没有元素,但是按索引访问时返回undefined
数组字面量允许末尾出现逗号
因此[ , ,]
的长度是2不是3
//扩展操作符
let a = [1,2,3];
let b = [0,...a,4];
console.log(b); //0,1,2,3,4,5
扩展操作符适用与任何可迭代对象(可以使用for/of循环遍历)
let digits = [..."0123456789"];
digits //=>["0","1",..."9:]
【数组 —> 集合 —> 数组】 实现数组的去重
集合对象是可迭代的,因此要去除数组中重复的元素,一种便捷的方式就是把数组转化为集合,然后再使用扩展操作符把这个集合转换回数组。
let letters = [..."hello world"]; //原数组
[...new Set(letters)] //=> ['h', 'e', 'l', 'o', ' ', 'w', 'r', 'd']
Array.of()
引入
在使用数值参数调用Array()构造函数时,这个参数指定的是数组的长度。
但是在使用一个以上的数值参数时,这些参数则会成为新数组的元素,这也就意味着使用Array()构造函数无法创建一个只包含一个特定数值元素的数组。
在ES6中,Array.of()函数可以解决这个问题
Array.of(10) //=>[10],可以创建一个只有一个数组元素的数组
Array.of(1,2,3) //=>[1,2,3]
Array.from()
Array.from()定义了一种给类数组对象创建真正的数组副本的机制,便于后续的操作
可以接受可选的第二个(函数)参数
那么在构建数组的时候,源对象的每个元素都会传入这个函数,这个函数的返回值将替代原始值成为新数组的元素(类似后面讲到的map
方法,但是在构建数组期间执行映射的效率要高于构建完毕后再映射为另一个新数组)
可以使用[ ] 操作符访问数组元素
JS数组是一个特殊对象,所有索引都是属性名。
与其他普通对象一样,JS会将数值数组索引转化为字符串(即1=>”1“)
可以使用负数或者非整数值来索引数组
此时,数值会转化为字符串,这个字符串会作为数组对象的属性名。
因此,JS数组没有所谓的越界说法。
直接给新索引赋值
push()
方法
在末尾添加一个或多个元素
与push()
执行相反操作的是pop()
,它删除最后一个元素并返回该元素
类似地,shift()
方法删除并返回数组的第一个元素
unshift()方法
实现将已有数组元素移动到更高索引位
delete
操作符
使用delete删除数组元素
不影响数组长度
删除后变为稀疏数组,不会把高位索引向下移动来填充空隙
let a =[1,2,3];
delete a[2];
console.log(2 in a); //false
console.log(a.length); //=>3:删除元素不影响数组长度,
splice()
方法
splice()是一个可以插入、删除或者替换数组元素的通用方法。
这个方法修改length属性并按照需要向更高位或更低位索引移动数组元素。
// for/of 输出list的所有元素
let list = [..."hello world"];
for (var item of list){
console.log(item);
}
如果 要对数组使用for /of 循环并且想知道每个元素的索引,可以使用数组的entries()方法和解构赋值
//[index,letter] of list.entries()
let list = [..."hello world"];
let everyother = "";
for (let [index,letter] of list.entries()){ //[index,letter]是每次循环解构赋值的对象
if (index % 2 === 0){ //取出奇数位字符
everyother += letter;
}
}
console.log(everyother); //=> hlowrd
//for in (索引) 输出list的所有元素
for (var i in list)
{
console.log(list[i]);
}
//forEach() 用于自身迭代的函数式方法(需要传入一个函数作用于每个非空元素)
let list = [..."hello world"];
delete list[0]; //forEach能感知稀疏数组,不会空元素调用函数
let uppercase = "";
list.forEach(letter=>{
uppercase += letter.toUpperCase();
})
console.log(uppercase); //=> ELLO WORLD
当然,使用老式的for循环可以遍历数组。
在嵌套循环或是其他性能攸关的场合,推荐使用以下的两种for循环形式,只会读取一次数组长度,而不是在每个迭代中都读取一次。
//老式for循环
//(正向)把数组长度保存到局部变量中
for (let i=0,len = list.length;i<len;i++){
console.log(list[i]);
}
//反向
for (let i = list.length-1;i>=0;i--){
console.log(list[i]);
}
以上两个例子是假定数组是稠密的,即所有元素都包含有效数据
如果不是这个情况,那么可以在使用每个元素前进行测试:
//跳过稀疏部分
for (let i =0,len = list.length;i<len;i++){
if (list[i] === undefined) continue;
console.log(list[i]);
}
下面介绍的方法用于迭代数组元素,他们会按顺序把数组的每个元素传给我们提供的函数,从而实现对数组的迭代、映射、过滤、测试和归并。
在这之前,先介绍这些方法的共同点:
所有方法都接受一个函数作为第一个参数,并且对数组的每个(或某些)元素都调用一次这个函数
这些方法都是可以感知稀疏数组的
通常情况下,我们提供的函数都会接受到3个参数,分别是
通常只需要这几个参数中的第一个,可以忽略第二和第三个的值。
forEach()
forEach()在调用我们指定的函数时会给他传3个参数:数组元素的值,数组元素的索引和数组本身。
如果只关心数组的值和索引,可以把函数写成接受两个参数的形式,忽略其他参数
//forEach()
let data = [1,2,3,4],sum=0;
//输出每个数组元素的下标以及对应的值
data.forEach((value,index)=>{ //接受数组的值和索引
console.log(`data[${index}] = ${value}`);
})
//输出如下:
//data[0] = 1
//data[1] = 2
//data[2] = 3
//data[3] = 4
【注意】:
map()
map()方法把调用它的数组的元素传给我们指定的函数,返回这个函数的返回值构成的数组
例如:
//map
let a = [1,2,3];
let b= a.map(x=>x*x);
console.log(a);
console.log(b);
由此可见:
filter()
filter方法返回一个数组。
该数组包含调用它的数组的子数组。传给它的函数应该是一个断言函数(返回true、flase)。
如果函数返回true,则传给这个函数的数组元素就是filter()最终返回的子数组的成员。实现过滤的效果:
例如:
//filter
let a = [5,4,3,2,1];
console.log(a.filter(x => x < 3)); //[2,1] : 返回的子数组元素为小于3的值
console.log(a.filter( (x,i) => i%2 === 0)); // [5,3,1] : 返回的子数组元素是索引为偶数的值
find()
与findIndex()
与filter()类似,表现为遍历数组,寻找断言函数返回真值的元素。
但是不同的是,这两个方法会在断言函数找到第一个元素时停止迭代。
此时,
如果没有找到匹配的元素:
every()
与some()
传入这两个方法的函数也是断言函数,并且方法本身是数组断言方法(最后返回true或false)
every()方法类似于 “全称量词” —任意
它只在断言函数对数组的所有元素都返回true时every( )才返回true
//every()
let a = [1,,2,3,4,5];z
let every1 = a.every( x => x<10);
console.log(every1); //true:所有元素都小于10
let every2 = a.every( x=> x%2 === 0);
console.log(every2); //false: 不是所有元素都是偶数
some()方法类似于 “存在”量词—存在
只要有一个元素让断言函数返回true,some( )就返回true
//some()
let a = [1,,2,3,4,5];
let some = a.some( x => x%2 === 0);
console.log(some); //true: 数组a中包含偶数
【注意】:every()和some()都采取了短路求值的方式(即在他们知道要返回什么的时候停止迭代数组)
some()在遇到断言函数第一次返回true时就返回true,全部断言都返回false时才返回false
every()则相反,在遇到断言函数第一次遇到false时返回false,全部断言都返回true时才返回true
reduce()
和reduceRight()
reduce和reduceRight方法使用我们指定的函数来归并数组元素,最终产生一个返回值。
在函数编程中,归并时一个常见操作,有时候也称为“注入(inject)”或"折叠(fold)"
来看下面这个例子:
//reduce
let a = [1,2,3,4,5];
//求和
console.log( a.reduce((x,y)=> x+y , 0) ); // =>15 : 0 +1 +2 +3 +4 +5
//求积
console.log( a.reduce((x,y) => x*y ,1 )); //=>120 :1*1*2*3*4*5
//求最大值
console.log( a.reduce( (x,y)=>(x>y)?x:y)); //=>5 :最大值
【分析】:
reduce接受两个参数:
第一个是执行归并操作的函数
此时使用的函数与前面介绍的函数不同。我们熟悉的三个参数(值、索引、数组本身)在这里作为
第二、第三、第四个参数,而第一个参数是目前为止归并操作的累计结果
第二个参数是可选的,指定了归并操作的初始值(如从0开始累加,从1开始累乘)
所以,在上面的求和、求积、求最大值的三个例子中,都可以不设定reduce()的第二个参数。
reduceRight()和reduce()类似,但是reduceRight是从高索引向低索引处理数组(从右到左)。
如果归并操作具有从右到左的结合性,那么可能就要用到reduceRight()
比如:
//reduceRight()
//计算2^(3^4)
let b = [2,3,4];
console.log( b.reduceRight( (exponent,base)=> Math.pow(base,exponent)) );
下面再介绍几个数组的提取、替换、填充和复制切片的方法。
slice()
slice()方法返回一个数组的切片(slice)或者子数组。
接受2个参数: 分别是切片的起止位置
【注意】:
起止位置是左闭右开的一个区间(包含起始位置,不包含终止位置)
所以子数组的元素个数为两个参数的差(都为正数的情况下)
如果只指定一个参数,返回的是数组从起点到数组末尾的所有元素
如果两个参数有出现负数,则这个负值是相对于数组长度指定数组元素
比如参数-1指定数组的最后一个元素
slice()不会修改调用它的数组。
【例子】:
//slice()
let a = [1,2,3,4,5];
console.log( a.slice(0,3)); //=>[0,1,2]
console.log( a.slice(0)); //=>[1,2,3,4,5]
console.log( a.slice(1,-1)); // =>[2,3,4]
splice()
splice()是对数组进行插入和删除(可以同时执行)的通用方法。
与slice()不同,splice会修改调用它的数组。
删除元素
splice()的前两个参数确定要删除的元素
//splice()
//删除数组元素
let a = [0,1,2,3,4,5];
a.splice(1,2); //从下标1开始删除2个元素:得到[0,3,4,5]
console.log(a); //splice会改变调用它的数组
添加元素
slice()在前两个参数后面还能添加任意多个的参数,表示要在第一个参数指定的位置插入到数组中的元素
例如:
//splice()
//同时在第一个参数指定的位置删除和添加元素
let a = [0,1,2,3,4,5];
a.splice(1,2,1,1,1); //从下标1开始删除2个元素:得到[0,1,1,1,3,4,5]
console.log(a); //splice会改变调用它的数组
fill()
fill方法用于在指定位置上填充数组
它可以接受三个参数(其中后两个可选):
省略参数和出现负数参数的情况的处理方式和slice()相同。
【例子】:
//fill()填充数组
let a = new Array(5); //创建一个长度为5的空数组
a.fill(0); //[0,0,0,0,0]
a.fill(9,1); //[0,9,9,9,9]
a.fill(8,2,-1) //[0,9,8,8,9]
copyWithin()
copyWithin()把数组切片复制到数组中的新位置。
它会就地修改并且返回修改后的数组,但是不会改变数组的长度。
//copyWithin()
let a = [1,2,3,4,5];
a.copyWithin(1); //[1,1,2,3,4]:把数组元素复制到索引1及之后(不改变数组长度前提下,所以5没有被复制过来)
console.log(a);
a.copyWithin(2,3,5); //[1,1,3,4,4]把最后两个元素复制到索引2
console.log(a);
数组实现与字符串的同名方法类似的indexof()
、lastIndexOf()
和includes()
方法,
此外还有sort()
和reverse()
方法用于对数组元素重新排序。
这两个方法从数组中搜索指定的值并返回第一个找到的元素的索引。
如果没找到返回 -1.
区别在于:lastIndexOf是从后向前搜索数组。
//indexOf和lastIndexOf
let a =[0,1,2,1,0];
console.log(a.indexOf(1)); //从前向后找第一个:索引为1
console.log(a.lastIndexOf(1)); //从后向前找第一个:索引为3
console.log(a.indexOf(3)); //没找到,返回-1
此外,这两个方法还能接受第二个可选的参数,指定从哪个位置开始搜索。
//指定第二个参数
let a =[0,1,2,1,0];
console.log(a.lastIndexOf(1)); //从后向前找第一个:索引为3
console.log(a.indexOf(1,2)); //从索引2开始向后找第一个值位1的元素,返回它的索引:3
【注意】:字符串也有indexOf和lastIndexOf,区别在于字符串方法第二个参数如果是负数会被当成0。
include()
接受一个参数,如果数组包含该值则返回true,否则返回false。
它并不告诉你值的索引,只告诉你是否存在。
sort()
sort()方法对数组实现就地排序并返回排序后的数组。
在不传参调用时,sort默认按照字母顺序对数组元素排序。
//sort()
let a =["banana","cherry","apple"];
a.sort();
console.log(a); //["apple","banana","cherry"]
如果要实现按照非字母顺序排序,必须要给sort()传递一个比较函数作为参数,这个比较函数决定它的两个参数排序后的前后位置:
如果第一个参数要出现在前面,比较函数应该返回一个< 0 的值,反之要返回一个 >0的值。
如果两个值相等,则返回0。
//带函数的sort
//返回值<0时第一个参数在第二个参数前面
let a = [33,4,1111,222];
console.log(a.sort()); //[1111,222,33,4]
//升序 a
a.sort( function(a,b){
return a-b
})
console.log(a); //[4,33,222,1111]
//降序 a-b>0 即返回值b-a<0
a.sort( function(a,b){
return b-a;
});
console.log(a); //[1111,222,33,4]
reverse()
就地反转数组元素的顺序,并返回反序后的数组。
//reverse()
let a = [1,2,3];
console.log(a.reverse()); //[3,2,1]
JS数组就介绍到这里啦!
J如果这篇文章对你有帮助的话希望三连下⭐✍支持一下博主
如果有什么建议的话可以在评论区留言哦!或者私信博主而也可以哦!✍