20 Array.map()
20.1 基本介绍
map()
方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
array.map(callbackFn)
array.map(callbackFn, thisArg)
输入参数:
callbackFn(element, index, array)
:生成新数组元素的函数。thisArg
(可选):执行callbackFn
时使用的this
值。
输出:一个新的数组,每个元素是原数组元素调用 callbackFn
后的返回值。
注意事项:
map()
不会修改原数组,是一个复制方法。- 对于稀疏数组,
map()
会跳过空槽,不会调用callbackFn
。 - 如果
callbackFn
修改了数组,则修改可能会影响后续的遍历。
20.2 手写实现
MyArray.prototype.map = function(callbackFn, thisArg) {
if (typeof callbackFn !== 'function') {
throw new TypeError(callbackFn + ' is not a function');
}
let result = new MyArray();
for (let i = 0; i < this.length; i++) {
if (!(i in this)) continue; // 跳过空槽
result[i] = callbackFn.call(thisArg, this[i], i, this);
}
result.length = this.length; // 保持长度一致
return result;
};
// 测试用例
let arr = new MyArray(1, 2, 3);
let mappedArr = arr.map(x => x * 2);
console.log(mappedArr); // [2, 4, 6]
let arr2 = new MyArray().concat([1, ,3]);
let mappedArr2 = arr2.map((x, index) => index);
console.log(mappedArr2); // [0, , 2]
难点总结:
- 处理稀疏数组:跳过空槽,不调用
callbackFn
,在结果数组中保留空槽。 - 返回新数组:需要返回与原数组同类型的新数组。
- 保持索引一致:结果数组的长度和索引应与原数组保持一致。
21 Array.pop()
21.1 基本介绍
pop()
方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
array.pop()
输入参数:无。
输出:被移除的元素。如果数组为空,则返回 undefined
。
注意事项:
pop()
方法会修改原数组,是一个修改方法。- 当数组为空时,返回
undefined
,数组长度仍为0
。
21.2 手写实现
MyArray.prototype.pop = function() {
// 数组为空时直接返回undefined
if (this.length === 0) {
return undefined;
}
let lastIndex = this.length - 1;
let lastElement = this[lastIndex];
// 使用delete关键字删除对象上的属性
delete this[lastIndex];
// 记得更新length
this.length = lastIndex;
return lastElement;
};
// 测试用例
let arr_3 = new MyArray(1, 2, 3);
let poppedElement = arr_3.pop();
console.log(poppedElement); // 3
console.log(arr_3); // [1, 2]
let emptyArr = new MyArray();
let poppedFromEmpty = emptyArr.pop();
console.log(poppedFromEmpty); // undefined
console.log(emptyArr); // []
难点总结:
- 更新
length
属性:需要手动更新数组的length
属性。 - 删除元素:使用
delete
操作符删除最后一个元素,数组的索引:值也可以看作是对象的属性进行删除。 - 处理空数组:当数组为空时,返回
undefined
。
22 Array.push()
22.1 基本介绍
push()
方法将一个或多个元素添加到数组的末尾,并返回新数组的长度。
array.push(element1)
array.push(element1, element2, ..., elementN)
输入参数:
elementN
:要添加到数组末尾的元素。
输出:数组添加新元素后的长度。
注意事项:
push()
方法会修改原数组,是一个修改方法。- 可以同时添加多个元素。
22.2 手写实现
MyArray.prototype.push = function(...args) {
let length = this.length >>> 0; // 确保 length 为非负整数\
// 剩余参数是数组的形式,遍历剩余参数,依次加入原数组
for (let i = 0; i < args.length; i++) {
this[length + i] = args[i];
}
// 记得更新数组长度
this.length = length + args.length;
// 返回数组长度
return this.length;
};
// 测试用例
let arr_4 = new MyArray(1, 2, 3);
let newLength = arr_4.push(4, 5);
console.log(newLength); // 5
console.log(arr_4); // [1, 2, 3, 4, 5]
难点总结:
- 处理
length
属性:需要正确更新数组的length
属性。 - 支持多个参数:
push()
可以接受多个参数,需要遍历args
。
23 Array.reduce()
23.1 基本介绍
reduce()
方法对数组中的每个元素执行一个 reducer 函数(升序执行),将其结果汇总为单个返回值。
array.reduce(callbackFn)
array.reduce(callbackFn, initialValue)
输入参数:
callbackFn(previousValue, currentValue, currentIndex, array)
:一个函数,用于执行数组中的每个元素,包含四个参数。initialValue
(可选):作为第一次调用callbackFn
时的第一个参数的值。如果没有提供初始值,则使用数组中的第一个元素。
输出:累加器最终的结果值。
注意事项:
- 如果没有提供
initialValue
,且数组为空,会抛出TypeError
。 - 如果没有提供
initialValue
,则从索引1
开始迭代,previousValue
为数组第一个元素。 - 对于稀疏数组,跳过空槽,但索引会递增。
- 不像其它方法,
reduce
方法不接受参数thisArg
23.2 手写实现
MyArray.prototype.reduce = function(callbackFn, initialValue) {
if (typeof callbackFn !== 'function') {
throw new TypeError(callbackFn + ' is not a function');
}
let length = this.length >>> 0;
let k = 0;
let accumulator;
if (arguments.length > 1) {
accumulator = initialValue;
} else {
// 找到第一个有效值作为初始值
while (k < length && !(k in this)) {
k++;
}
if (k >= length) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = this[k++];
}
for (; k < length; k++) {
if (k in this) {
accumulator = callbackFn(accumulator, this[k], k, this);
}
}
return accumulator;
};
// 测试用例
let arr_5 = new MyArray(1, 2, 3, 4);
let sum = arr_5.reduce((prev, curr) => prev + curr);
console.log(sum); // 10
let arr_6 = new MyArray().concat([1, , 3, 4]);
let sum2 = arr_6.reduce((prev, curr) => prev + curr, 0);
console.log(sum2); // 8
难点总结:
- 处理初始值:需要正确处理是否提供了
initialValue
。 - 处理稀疏数组:跳过空槽,但索引和循环计数需要正确。
- 错误处理:当数组为空且未提供
initialValue
时,需要抛出错误。
24 Array.reduceRight()
24.1 基本介绍
reduceRight()
方法对数组中的每个元素执行一个 reducer 函数(从右到左),将其结果汇总为单个返回值。
array.reduceRight(callbackFn)
array.reduceRight(callbackFn, initialValue)
输入参数:
callbackFn(previousValue, currentValue, currentIndex, array)
:一个函数,用于执行数组中的每个元素,包含四个参数。initialValue
(可选):作为第一次调用callbackFn
时的第一个参数的值。如果没有提供初始值,则使用数组中的最后一个元素。
输出:累加器最终的结果值。
注意事项:
- 如果没有提供
initialValue
,且数组为空,会抛出TypeError
。 - 如果没有提供
initialValue
,则从索引length - 2
开始迭代,previousValue
为数组最后一个元素。 - 对于稀疏数组,跳过空槽,但索引会递减。
24.2 手写实现
MyArray.prototype.reduceRight = function(callbackFn, initialValue) {
if (typeof callbackFn !== 'function') {
throw new TypeError(callbackFn + ' is not a function');
}
let length = this.length >>> 0;
let k = length - 1;
let accumulator;
if (arguments.length > 1) {
accumulator = initialValue;
} else {
// 找到最后一个有效值作为初始值
while (k >= 0 && !(k in this)) {
k--;
}
if (k < 0) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = this[k--];
}
for (; k >= 0; k--) {
if (k in this) {
accumulator = callbackFn(accumulator, this[k], k, this);
}
}
return accumulator;
};
// 测试用例
let arr_7 = new MyArray(1, 2, 3, 4);
let result = arr_7.reduceRight((prev, curr) => prev - curr);
console.log(result); // -2 ((4 - 3) - 2) - 1
let arr_8 = new MyArray().concat([1, , 3, 4]);
let result2 = arr_8.reduceRight((prev, curr) => prev + curr, 0);
console.log(result2); // 8
难点总结:
- 处理初始值:需要正确处理是否提供了
initialValue
。 - 处理稀疏数组:跳过空槽,但索引和循环计数需要正确。
- 错误处理:当数组为空且未提供
initialValue
时,需要抛出错误。
25 Array.reverse()
25.1 基本介绍
reverse()
方法将数组中元素的位置颠倒,并返回该数组。第一个元素会成为最后一个,最后一个元素会成为第一个。
array.reverse()
输入参数:无。
输出:颠倒顺序后的数组,原数组已被修改。
注意事项:
reverse()
方法会修改原数组,是一个修改方法。- 对于稀疏数组,
reverse()
会保留空槽的位置。
25.2 手写实现
MyArray.prototype.reverse = function() {
let length = this.length >>> 0;
let middle = Math.floor(length / 2);
for (let i = 0; i < middle; i++) {
// 判断存在性,为是否删除元素提供依据
let lowerExists = i in this;
let upperExists = (length - 1 - i) in this;
// 根据存在性获取值
let lowerValue = lowerExists ? this[i] : undefined;
let upperValue = upperExists ? this[length - 1 - i] : undefined;
// 进行交换流程
if (lowerExists) {
this[length - 1 - i] = lowerValue;
} else {
// 如果交换过来的值是空槽,删除对应的属性
delete this[length - 1 - i];
}
if (upperExists) {
this[i] = upperValue;
} else {
delete this[i];
}
}
return this;
};
// 测试用例
let arr_9 = new MyArray(1, 2, 3, 4);
arr_9.reverse();
console.log(arr_9); // [4, 3, 2, 1]
let arr_10 = new MyArray().concat([1, , 3]);
arr_10.reverse();
console.log(arr_10); // [3, , 1]
难点总结:
- 处理稀疏数组:需要正确处理空槽的位置,交换元素时如果元素不存在,那么使用
delete
操作符删除对应位置上的元素。 - 原地修改:需要在原数组上进行修改,不能创建新数组,所以需要提前对元素的存在性和值进行判断。
- 交换元素:需要注意元素的存在性,避免错误覆盖或删除。