一、引言
由于对JavaScript中一些数组API的使用及原理不够熟悉,在此进行一些数组Api的练习,首先根据数组Api是否会改变原数组进行分类,然后在分类中进行API的实践,最后尝试根据自己的理解重写这些API。
二、会改变原数组的API
push
意义: 将指定的元素添加到数组的末尾,并返回新数组的长度。
实际应用:
// 例子:向原数组arr,添加两个新元素
const arr = [1,2,3,4,5,6];
const pushResult = arr.push(7,8); // push的参数可以为多个,用逗号分隔开
console.log(pushResult); // push的返回值是新数组的长度,此处是8
API重写:
Array.prototype.myPush = function () {
// arguments是一个对应于传递给函数的参数的类数组对象
for (let i = 0; i < arguments.length; i++) {
this[this.length] = arguments[i];
}
return this.length; // 返回新数组的长度
};
pop
意义: 从数组中删除最后一个元素,并返回该元素的值。 需注意的是: 如果对一个空数组调用pop(),它会返回undefined。
实际应用:
const arr = [1,2,3,4,5];
const popElemenet = arr.pop();
console.log(popElement); // 5
API重写:
Array.prototype.myPop = function () {
if (this.length) {
const popElement = this[this.length - 1];
delete this[this.length - 1];
this.length--;
return popElement;
}
};
shift
意义: 从数组中删除第一个元素,并返回该元素的值。 需注意的是: shift( )将第一个元素删除后,会后续元素的下标依次向前移动。如果对一个空数组调用shift(),它会返回undefined。
实际应用:
const arr = [1, 2, 3, 4, 5];
const shiftElement = arr.shift();
console.log(shiftElement); // 1
API重写:
Array.prototype.myShift = function() {
if (this.length) {
const shiftElement = this[0];
for (let index = 0; index < this.length - 1; index++) {
this[index] = this[index + 1];
}
this.length--;
return shiftElement;
}
};
unshift
意义: 将指定元素添加到数组的开头,并返回新数组的长度。
实际应用:
const arr = [3, 4, 5, 6];
const newArrLength = arr.unshift(1, 2);
console.log(newArrLength); // 6
重写API:
Array.prototype.myUnshift = function () {
const argumentsLen = arguments.length;
this.length += argumentsLen;
for (let index = this.length - 1; index >= 0; index--) {
if (index > argumentsLen - 1) {
this[index] = this[index - argumentsLen];
} else {
this[index] = arguments[index];
}
}
return this.length;
};
splice
意义: 移除或替换数组中已存在的元素,或是给数组添加新的元素。 参数分析:
start:从零开始计算的索引,表示要开始改变数组的位置。
1.如果-array.length < start < 0,那么开始索引为start + array.length。
2.如果start < -array.length,那么开始索引为0.
3.如果start >= array.length,则不会删除任何元素,此次调用会被理解为向数组添加新的元素。
4.如果start被省略,则不会删除任何元素。
deleteCount:表示数组中要从start开始删除的元素数量。(可选参数)
1.如果省略deleteCount或者deleteCount>array.length-start,那么从start到数组的最后一个元素将被删除。
2.如果deleteCount的值是0或是负数,则不会删除任何元素。在这种情况下,你应该至少指定一个新元素。
item1、...、itemN:从start开始要加入到数组的中的元素。(可选参数)
返回值:调用splice方法将会返回一个由被删除元素组成的数组,如果没有删除任何元素,则返回一个空数组。
实际应用:
const arr = [1,2,3,7,8];
const spliceArr = arr.splice(3,0,4,5,6);
console.log(spliceArr); // []
console.log(arr); // [1,2,3,4,5,6,7,8]
重写API:
Array.prototype.mySplice = function (start, deleteCount, ...items) {
let deleteItems = [],
startIndex = 0,
actualDeleteCount;
if (Object.is(arguments.length, 0)) return deleteItems;
const length = this.length;
startIndex =
start >= 0 ? Math.min(start, length) : Math.max(length + start, 0);
actualDeleteCount = Object.is(arguments.length, 1)
? length - startIndex
: Math.min(Math.max(+deleteCount, 0), length - startIndex);
deleteItems = this.slice(startIndex, startIndex + actualDeleteCount);
const otherItems = this.slice(startIndex + actualDeleteCount);
[...items, ...otherItems].forEach((item) => (this[startIndex++] = item));
this.length = startIndex;
return deleteItems;
};
reverse
意义: 反转数组中的元素,并返回该数组。
实际应用:
const arr = [1, 2, 3, 4, 5];
const reverseArr = arr.reverse();
console.log(reverseArr);// [5,4,3,2,1]
重写API:
Array.prototype.myReverse = function () {
let left = 0,
right = this.length - 1;
while (left < right) {
const temp = this[left];
this[left] = this[right];
this[right] = temp;
left++;
right--;
}
return this;
};
三、不会改变原数组的API
includes
意义: 用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false。 参数分析:
| includes(searchElement,fromIndex) | 意义 |
|---|---|
| searchElement | 需要查找的值 |
| fromIndex (可选) | 开始搜索的索引(从0开始) |
实际应用:
const arr = [1, 2, 3, 4, 5];
const result = arr.includes(3);
console.log(result);// true
重写API:
Array.prototype.myIncludes = function (searchElement, fromIndex = 0) {
const length = this.length;
const startIndex = Math.max(
fromIndex >= 0 ? fromIndex : length + fromIndex,
0
);
for (let index = startIndex; index < length; index++) {
if (this[index] === searchElement) {
return true;
}
}
return false;
};
indexOf
意义: 返回数组中第一次出现给定元素的下标,如果不存在则返回-1. 参数分析:
| indexOf(searchElement,fromIndex) | 意义 |
|---|---|
| searchElement | 需要查找的值 |
| fromIndex (可选) | 开始搜索的索引(从0开始) |
实际应用:
const arr = [1,2,3,4,5];
const findItemIndex = arr.indexOf(2); // 1
重写API:
Array.prototype.myIndexOf = function (searchElement, fromIndex = 0) {
let findIndex = -1,
actualFromIndex;
if (Object.is(arguments.length, 0)) return findIndex;
if (fromIndex >= this.length) return findIndex;
actualFromIndex = Object.is(arguments.length, 1)
? 0
: Math.min(Math.max(0, fromIndex + this.length), findIndex);
for (let index = actualFromIndex; index < this.length; index++) {
if (this[index] === searchElement) {
findIndex = index;
}
}
return findIndex;
};
slice
意义: 提取一个数组中的某些元素,并返回一个包含被提取元素的新数组,具体提取哪些元素,由start和end参数决定。
| slice(start,end) | 意义 |
|---|---|
| start(可选) | 提取起始处的索引(默认为0) |
| end(可选) | 提取终止处的索引(默认为0) |
实际应用:
const arr = [1,2,3,4,5];
const sliceArr = arr.slice(2,4);
console.log(sliceArr); // [3,4]
重写API:
Array.prototype.mySlice = function (start = 0, end) {
let sliceArr = [];
if (start > this.length) return sliceArr;
let startIndex, endIndex;
startIndex = Math.min(Math.max(0, start + this.length), start);
endIndex = Math.min(Math.max(0, end + this.length), end);
if (endIndex > this.length || !endIndex) endIndex = this.length;
if (startIndex > endIndex) return sliceArr;
for (let index = startIndex; index < endIndex; index++) {
sliceArr[sliceArr.length] = this[index];
}
return sliceArr;
};
concat
意义: 用于合并两个或多个数组,此方法不会改变原数组,而是返回一个新数组。
实际应用:
const arr = [1,2,3];
const concatArr1 = arr.concat([4],[5]); // [1,2,3,4,5]
const concatArr2 = arr.concat(4,5); // [1,2,3,4,5]
重写API:
Array.prototype.myConcat = function (...args) {
const newArr = [...this];
args.forEach((item) => {
if (Array.isArray(item)) {
newArr.push(...item);
} else {
newArr.push(item);
}
});
return newArr;
};
map
意义: 此方法将返回一个新数组,新数组中的元素由原数组中的每一个元素调用map提供的函数参数后得到的返回值组成的。
实际应用:
const arr = [1,2,3,4,5];
const mapArr = arr.map((item) => item + 1);
console.log(mapArr);// [2,3,4,5,6]
重写API:
Array.prototype.myMap = function (callback) {
const res = [];
for (let index = 0; i < this.length; index++) {
res.push(callback(this[index], index, this));
}
return res;
};
filter
意义: 过滤数组中的某些元素,根据所给的筛选方法,对原数组中的每一个元素进行筛选,并返回一个由筛选通过的元素组成的新数组。
实际应用:
const arr = [1,2,3,4,5];
const filterArr = arr.filter((item) => item % 2 === 0);
console.log(filterArr);// [2,4]
重写API:
Array.prototype.myFilter = function (callback) {
const res = [];
for (let index = 0; i < this.length; index++) {
callback(this[index], index, this) && res.push(this[index]);
}
return res;
};
reduce
意义: 对数组中的每一个元素按序执行一个提供的reducer函数,每一次运行reducer会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
实际应用:
const arr = [1,2,3,4,5];
const sum = arr.reduce((acc, current) => acc + current, 0);
console.log(sum);// 15
重写API:
Array.prototype.myReduce = function (callback, ...arg) {
let pre,
start = 0;
if (arg.length) {
pre = arg[0];
} else {
pre = this[0];
start = 1;
}
for (let index = start; index < this.length; index++) {
pre = callback(pre, this[index], index, this);
}
return pre;
};
forEach
意义: 对数组中的每一个元素执行一次给定的函数,返回值为undefined。
实际应用:
const arr = [1,2,3];
const callbackFn = (element, index) => console.log(element, index);
arr.forEach(callbackFn);
// 1 0
// 2 1
// 3 2
重写API:
Array.prototype.myForEach = function (callback) {
for (let index = 0; index < this.length; index++) {
callback(this[index], index, this);
}
};
join
意义: 将一个数组中的所有元素连接成一个字符串,并返回这个字符串。
实际应用:
const arr = [1,2,3,4,5];
const joinArraryStr = arr.join('-');
console.log(joinArrayStr); // "1-2-3-4-5"
重写API:
Array.prototype.myJoin = function (str = ",") {
let resultStr = "";
for (let index = 0; index < this.length; index++) {
resultStr += `${this[i]}${str}`;
}
return resultStr.slice(0, resultStr.length - 1);
};
flat
意义: 根据指定的深度递归地将所有子数组元素都拼接到新的数组中,并返回拼接后的新数组。
实际应用:
const arr = [1, [2], 3, [4, [5]]];
console.log(arr.flat(1)); // [1,2,3,4,[5]]
console.log(arr.flat(2)); // [1,2,3,4,5]
重写API:
Array.prototype.myFlat = function (dep = 1) {
return this.reduce((acc, val) => {
return acc.concat(
Array.isArray(val) && dep > 0
? val.myFlat(dep - 1)
: Array.isArray(val)
? [val]
: val
);
}, []);
};