数组的常用方法
数组的添加和删除
push()与pop()
这两个方法的功能与以前学的出栈和入栈方法是一样的,都是在原数组中操作的,并非生成一个修改后的新数组。push()在数组的尾部添加一个或多个元素,并返回数组新的长度;pop()删除数组的最后一个元素,减少数组长度并返回删除的值。
unshift()与shift()
类似于push()和pop(),只不过它们是在数组的头部进行添加和删除操作。unshift()是在数组头部添加一个或多个元素(将已存在的其他元素移动到更高索引的位置),返回数组的新长度;shift()则是删除数组的第一个元素并将其返回(删除后数组的其他元素会下移一个位置填充数组头部的空缺)。注意,使用unshift()一次性插入多个元素与多次调用unshift()插入多个元素的表现是不一样的,如下所示:
var a=[]; a.unshift(1); a.unshift(2); alert(a.toString());//a=[2,1] a.unshift(3,4); alert(a.toString());//a=[3,4,2,1],并非是像前面那样一个一个向头部插入 delete a[0]; alert(a.toString());//a=[,4,2,1],使用delete删除元素只是将该位置元素置为undefined,并不是从数组中移除
在这里可以看到,使用delete删除数组元素并不会改数组的length属性,也不会将元素从高索引处下移来填充空缺位。这样,使用delete删除元素会使得数组变成稀疏数组。
splice()
该方法是在数组中插入或删除元素的通用方法,它会修改原数组的元素,并非是创建一个修改后的新数组。splice()有多个参数,功能如下:
- 第一个参数:指定了插入和(或)删除的起始位置
- 第二个参数:指定了要删除的元素个数
- 随后任意多个参数:指定了需要插入的元素
因此,根据提供参数的个数,splice()方法有多个实现功能:
- 若仅提供了一个参数splice(a),splice()执行的是删除操作,表示从参数指定的起始位a开始到数组末尾的所有元素都将被删除;
- 若提供了两个参数splice(a,b),仍是删除操作,表示从第一个参数指定的起始位a开始,删除b个元素;
- 若提供两个以上参数,splice()会同时完成删除和插入操作(如果将第二个参数设为0,则只执行插入操作)。
var a=[1,2,3,4,5,6,7,8]; var n1=a.splice(4);//n1=[5,6,7,8],a=[1,2,3,4] var n2=a.splice(1,2);//n2=[2,3],a=[1,4] var n3=a.splice(1,0,'a','b');//n3=[],a=[1,'a','b',4] var n4=a.splice(1,2,[1,2],3);//n4=['a','b'],a=[1,[1,2],3,4]
concat()
该方法用于在数组后面拼接新的元素,所要添加的元素作为参数传递到concat()方法中,这个方法不会修改原数组,返回的是新创建的拼接后的数组。值得注意的是:若参数包含数组,则拼接进来的是参数数组的元素,而不是将整个数组作为一个整体拼接进来,但它又不会递归扁平化数组的数组(也即作为参数的数组内部若还嵌套着数组,此时该嵌套的数组将作为一个整体拼接进来)。
var a=[1,2,3]; a.concat(4,5); //返回[1,2,3,4,5] a.concat([4,5]);//返回[1,2,3,4,5] a.concat([4,5],[6,[7,8]]);//返回[1,2,3,4,5,6,[7,8]],length=7
数组转换为字符串输出
join()
该方法将数组中所有元素都转换为字符串并连接在一起,返回最后生成的字符串。它可以接收一个字符串参数用来作为分隔符,若不指定分隔符则默认使用逗号。
toString()
与不使用任何参数调用join()方法返回的字符串是一样的。
var a=[1,2,3]; a.join();// 输出"1,2,3" a.join(" "); //输出"1 2 3" a.toString(); //输出"1,2,3"
数组排序
reverse()
该方法将数组中元素的顺序颠倒,返回逆序的数组。它在原数组的基础上改变,并非创建新的逆序后的数组。
sort()
该方法将数组中的元素按字母表顺序排序并返回排序后的数组。同样,它也是在原数组的基础上进行更改。若想要按照其他方式进行排序,则可以向sort()方法传递一个比较函数。
var a=[33,4,1111,222]; a.sort(); //返回[1111,222,33,4],并非是我们以为的按数值大小排序 a.sort(function(a,b){ //按数值从小到大排序 return a-b; }); a.sort(function(a,b){ //按数值从大到小排序 return b-a; });
数组切割
slice()
该方法返回指定数组的一个片段或子数组,不会修改原数组。
var a = [1,2,3,4,5]; a.slice(0,3);//返回[1,2,3] a.slice(3);//返回[4,5] a.slice(1,-1);//返回[2,3,4] a.slice(-3,-2);//返回[3]
map()
该方法接收两个参数:
- 一个是callback函数,该方法会给原数组中的每个元素都执行一次callback函数,函数每次执行后的返回值依次作为新数组的元素。
- 另一个是thisArg(可选),该参数的值作为callback函数的上下文,也即callback函数的this指针。
callback函数可以使用三个参数:
- currentValue:正在处理的当前元素;
- index(可选):正在处理的当前元素的索引;
- array(可选):调用的数组;
误区
我们使用parseInt()方法时,通常认为该函数会默认将传入的字符串转换为十进制整数(在MDN文档中,强烈要求使用parseInt()时传入第二个参数作为基数)。观察下面代码可以发现,执行结果并非我们所预期的那样:
const arr = ["1", "2", "3"].map(parseInt); //arr = [1, NaN, NaN]
因为在这里,parseInt接收了两个参数,第一个是数组中的元素作为传入的要转换的字符串,第二个是数组元素的索引作为传入parseInt的基。迭代步骤如下:
// parseInt(string, radix) -> map(parseInt(value, index)) /* first iteration (index is 0): */ parseInt("1", 0); // 1 /* second iteration (index is 1): */ parseInt("2", 1); // NaN /* third iteration (index is 2): */ parseInt("3", 2); // NaN
因此,应该指明传入parseInt()中的参数个数
const arr = ['1', '2', '3'].map( str => parseInt(str) ); //arr = [ 1, 2, 3 ]
手写实现map()方法
(假设调用该方法的数组不是稀疏数组,也即索引值连续)
/** * @param {Array} callback : 要执行的函数 * @param {obj} context : callback函数的this对象 * @return {Array} */ Array.prototype.myMap = function (callback,context){ if(this===null){ throw new TypeError('this is null or not defined') } if(typeof callback !=='function'){ throw new TypeError(callback + 'is not a function') } const arr = this; //要与context区分开,这里的this指向callback函数的直接调用者(原数组) const newArray = new Array(arr.length); for(let i = 0;i<arr.length;i++){ newArray[i] = callback.call(context,arr[i],i,arr); //这里传入的context会作为callback函数的this对象 } return newArray; }
reduce()
该方法接收两个参数:
- 一个callback函数。数组中的每个元素按序执行callback函数,每一次执行的返回值会作为previousValue参数传入下一个元素的callback函数;
- 一个initialvalue(可选)初始值。若指定了初始值,则callback函数从索引为0的元素开始执行(previousValue=initialValue,currentValue=arr[0]);若没有指定初始值,则将索引为0的元素作为previousValue,callback函数从索引为1的元素处开始执行。
callback函数可以使用三个参数:
- previousValue:上一次调用callback函数时的返回值;
- currentValue:正在处理的元素;
- currentIndex:正在处理的元素的索引;
- array:当前遍历的对象;
注意:当数组仅有一个元素且没有提供初始值innitailValue,或者提供了innitialValue而数组为空时,此唯一值将被直接返回,不会执行callback
使用reduce()实现map()
Array.prototype.mapUsingReduce = function (callback,context){ let arr = this; let newArray = []; return arr.reduce((pre,cur,index)=>{ pre[index] = callback.call(context,cur); return pre; },newArray) }
使用ruduce()按顺序运行Promise
function p1() { return new Promise((resolve, reject) => { resolve(5) }) } function p2(a) { return new Promise((resolve, reject) => { resolve(a * 2) }) } function f3(a) { return a * 3; } function p4(a) { return new Promise((resolve, reject) => { resolve(a * 4) }) } const promiseArr = [p1, p2, f3, p4] /** * @param {Array} arr : 包含多个Promise的列表 * @param {Number} init : 初始值,封装成一个Promise。若没有指定,则会将undefined封装成一个Promise. * @return {Promise} */ function runPromiseInSequence(arr,init) { return arr.reduce((pre,cur)=>{ return pre.then(cur) },Promise.resolve(init)) } runPromiseInSequence(promiseArr,2).then(console.log)
首先,要明确各个函数都是异步操作,因此我们在执行reduce语句时,每次执行的返回值都是<pending>状态的Promise,整个循环实际上相当于Promise.then()的链式调用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程