用法
pop 方法可以弹出数组最后一个元素,并将其作为返回值
const arr = [1,2,3] arr.pop() // 返回移除的元素 5,数组变成 [1,2,3,4]
实现
Array.prototype.myPop = function(){
let arr = this
let returnValue = arr[arr.length - 1]
arr.length--
return returnValue
}
用法
push 方法可以往数组末尾添加任意多个元素,并将数组长度作为返回值:
const arr = [1,2,3] arr.push(4,5) // 返回最终的数组长度 5,数组变成 [1,2,3,4,5]
实现
Array.prototype.myPush = function(...args){
let arr = this
for(let el of args){
arr[arr.length] = el
}
return arr.length
}
用法
shift 方法可以从数组头部弹出一个元素,并将其作为返回值:
const arr = [3,4,5] arr.shift() // 返回移除的元素 1,数组变成 [2,3,4,5]
实现
Array.prototype.myShift = function(){
let arr = this
let returnValue = arr[0]
for(let i = 1;i < arr.length;i++){
arr[i-1] = arr[i]
}
arr.length--
return returnValue
}
用法
unshift 方法可以往数组头部添加任意多个元素,并将数组长度作为返回值:
const arr = [3,4,5] arr.unshift(1,2) // 返回最终的数组长度 5,数组变成 [1,2,3,4,5]
实现
Array.prototype.myUnshift = function(...args){
let arr = this
if(args.length > 0){
let len1 = arr.length,len2 = args.length
// k 代表数组最后一个元素的下标
let k = len1 + len2 - 1
for(let i = len1 - 1;i >= 0;i--){
arr[k--] = arr[i]
}
for(let i in args){
arr[i] = args[i]
}
}
return arr.length
}
用法
reverse 将原数组进行反转,最终返回原数组
[1,2,3].reverse() // [3,2,1]
实现
Array.prototype.myReverse = function(){
let arr = this
let k = arr.length - 1
for(let i = 0;i < Math.floor(arr.length/2);i++){
let temp = arr[i]
arr[i] = arr[k]
arr[k--] = temp
}
return arr
}
用法
reverse 也算是一种排序方法,但显然它不够灵活,于是有了 sort 方法。
undefined 时,默认的排序规则是:将每个元素转化为字符串,再将它们按照 Unicode 编码从小到大排序。其中,null 会转化为 "null",undefined 固定排在数组最后const arr = [1,2,5,10] // 没有传入函数,会对每个元素调用 toString,比较字符的 unicode 编码,因此 "10"<"2" arr.sort() // [1,10,2,5] // 传入比较函数,从小到大排序 arr.sort((a,b) => ab?1:0) // [1,2,5,10] arr.sort((a,b) => a-b) // [1,2,5,10] // 传入比较函数,从大到小排序 arr.sort((a,b) => a>b?-1:a实现
Array.prototype.mySort = function(...args){ let arr = this // 判断规则,判断 x 是否应该放在 y 的前面 function shouldBefore(x,y){ // 如果没传参或者传了 undefined if(args.length == 0 || args.length != 0 && typeof args[0] === 'undefined'){ return String(x) < String(y) } // 如果传函数 else { let fn = args[0] return fn(x,y) < 0 ? true : false } } // 如果传参但是没传函数或者 undefined if(typeof args[0] != 'function' && typeof args[0] != 'undefined'){ throw new TypeError("The argument msut be undefined or a function") } else { for(let i = 0;i < arr.length - 1;i++){ for(let j = 0;j < arr.length - 1 - i;j++){ if(shouldBefore(arr[j+1],arr[j])){ // 两数交换 let temp = arr[j] arr[j] = arr[j+1] arr[j+1] = temp } } } } return arr }splice
用法
splice 可以做三种事:删除元素、添加元素、替换元素。
start、删除的元素个数num ,以及添加的元素 item1、item2、...start 可以是正数或者负数。如果是正数且超过数组长度,则无视删除操作,直接把需要添加的元素添加到数组末尾;如果是负数,且负数绝对值小于数组长度,则将负数与长度相加作为 start,否则将 0 作为 startnum 可以是正数或者负数。如果没有传 num,或者 num 是正数且超过 start 往后的元素个数(包含 start),则将 start 和它后面所有元素删除;如果 num 是 0 或者负数,则不删除任何元素const arr = [1,2,3,4,5] // 删除:在索引1这里操作,删除2个元素 arr.splice(1,2) // 返回 [2,3],arr 变成 [1,4,5] // 添加:在索引1这里操作,删除0个元素,添加2个元素(注意是插入到索引1前面,不是后面) arr.splice(1,0,"a","b") // 返回 [],arr 变成 [1,"a","b",2,3,4,5] // 替换:删除+添加就是替换 arr.splice(1,2,"a","b") // 返回 [1,"a","b",4,5]
实现
Array.prototype.mySplice = function(...args){
let arr = this
let len = arr.length
let res = []
function computeStart(start){
return start >= 0 ? start : Math.abs(start) < len ? start + len : 0
}
function computeDeleteNum(args,start){
return args.length < 2 ?
len - start : args[1] > 0 ? Math.min(args[1],len - start) : 0
}
function sliceArray(arr,separator){
let arr1 = [],arr2 = []
for(let i = 0;i < arr.length;i++){
i < separator ? arr1.push(arr[i]) : arr2.push(arr[i])
}
// 清空原数组
arr.length = 0
return [arr1,arr2]
}
// 如果有传参数
if(args.length > 0){
// 确定 start 和 deleteNum 的取值
let start = computeStart(args[0])
let deleteNum = computeDeleteNum(args,start)
// 如果 start 已经大于等于数组长度,则只需关注是否有添加元素,无需进行后续操作
if(start >= len){
if(args.length > 2){
for(let i = 2;i < args.length;i++){
arr.push(args[i])
}
}
} else {
// 以 start 为界分割原数组
let [arr1,arr2] = sliceArray(arr,start)
// 如果有需要,就删除元素
if(deleteNum != 0){
for(let i = 0;i < deleteNum;i++){
// 把删除的元素放进返回的 res 数组中
res.push(arr2.shift())
}
}
// 如果有需要,就添加元素
if(args.length > 2){
for(let i = 2;i < args.length;i++){
arr1.push(args[i])
}
}
const tempArr = [...arr1,...arr2]
for(let el of tempArr){
arr.push(el)
}
}
}
return res
}
PS:个人感觉 splice 的实现算是这几个里比较麻烦的,因为需要考虑很多情况。上面的代码已经通过 MDN 的全部测试用例,但还有不少需要优化的地方。
用法
用某个值替换(填充)数组中的全部值或者部分值:
toFill,begin 和 end。toFill 表示填充元素,不传则为 undefined;begin 表示开始填充位置,默认从数组第一个元素开始;end 表示结束填充位置(该位置不填充),默认等于数组长度begin 可以是正数或者负数。如果是负数且绝对值小于数组长度,则将其与长度相加作为 begin,若大于数组长度,则取 0 作为 beginend 可以是正数或者负数,如果是正数且超过数组长度,则取数组长度作为 begin;如果是负数且绝对值小于数组长度,则将其与长度相加作为 endconst arr = [0,0,0,0,0] arr.fill(5) // [5,5,5,5,5] arr.fill(5,2) // [0,0,5,5,5] arr.fill(5,2,4) // [0,0,5,5,0] arr.fill(5,-3,-1) // [0,0,5,5,0] 负值索引 => 负值索引 + 数组长度 arr.fill(5,-100,-90) // 越界,无效 arr.fill(5,100,90) // 越界,无效 arr.fill(5,4,2) // 反向,无效
实现
Array.prototype.myFill = function(toFill,begin = 0,end = this.length){
let arr = this
let len = arr.length
begin = begin >= 0 ? begin : Math.abs(begin) < len ? begin + len : 0
end = end >= 0 ? Math.min(end,len) : Math.abs(end) < len ? end + len : end
for(;begin < end;begin++){
arr[begin] = toFill
}
return arr
}
用法
复制数组的某个部分,顶替数组中的某些元素:
target 表示开始操作的位置,begin 和 end 共同决定需要复制的范围(不包括 end)target 开始的元素const arr = [0,1,2,3,4,5,6,7,8] arr.copyWithin(4) // [0,1,2,3,0,1,2,3,4] 缺省范围为整个数组 arr.copyWithin(4,7) // [0,1,2,3,7,8,6,7,8] arr.copyWithin(4,6,9) // [0,1,2,3,6,7,8,7,8] // 对于负值索引、反向索引和越界索引的处理,和 fill 方法类似
实现
Array.prototype.myCopyWithin = function(target = 0,begin = 0,end = this.length){
let arr = this
let len = arr.length
let copyArr = []
let m = 0,n = 0
target = target >= 0 ? target : Math.abs(target) < len ? target + len : 0
begin = begin >= 0 ? begin : Math.abs(begin) < len ? begin + len : 0
end = end >= 0 ? Math.min(end,len) : Math.abs(end) < len ? end + len : end
// 把需要复制的元素放到 copyArr 数组中
for(;begin < end;begin++){
copyArr[m++] = arr[begin]
}
let _len = copyArr.length < len - target ? target + copyArr.length : len
// 用 copyArr 数组从 target 开始覆盖原数组
for(;target < _len;target++){
arr[target] = copyArr[n++]
}
return arr
}