12 Array.flat()
12.1 基本介绍
flat()
方法按照指定的深度递归遍历数组,并将所有元素与子数组中的元素合并为一个新数组返回。
array.flat(depth)
输入参数:
depth
(可选):指定要提取嵌套数组的结构深度,默认值为1
。
输出:一个包含将数组元素与子数组元素合并的新数组。
注意事项:
- 该方法不会修改原数组,是一个复制方法。
- 对于稀疏数组,空槽会被跳过,不会出现在返回的新数组中。
- 如果
depth
为Infinity
,则会完全扁平化数组。 flat
方法是通用的
12.2 手写实现
MyArray.prototype.flat = function(depth = 1) {
const result = [];
(function flatten(array, depth) {
for (let i = 0; i < array.length; i++) {
if (!(i in array)) continue; // 跳过空槽
const value = array[i];
if (Array.isArray(value) && depth > 0) {
flatten(value, depth - 1);
} else {
result.push(value);
}
}
})(this, depth);
return result;
};
// 测试用例
let arr1 = new MyArray(1, 2, [3, 4]);
console.log(arr1.flat()); // [1, 2, 3, 4]
let arr2 = new MyArray(1, 2, [3, [4, 5]]);
console.log(arr2.flat()); // [1, 2, 3, [4, 5]]
console.log(arr2.flat(2)); // [1, 2, 3, 4, 5]
let arr3 = new MyArray().concat([1, ,], 3, [4, , 6]);
console.log(arr3.flat()); // [1, 3, 4, 6]
难点总结:
- 递归处理:需要根据指定的深度递归展开数组,实现一个递归函数flatten(),每次传入需要递归的数组以及递归的深度 - 1。递归停止条件就是深度为0或者不是数组元素,那么就将结果存入res数组。
- 处理稀疏数组:空槽会被跳过,不出现在结果数组中。
13 Array.flatMap()
13.1 基本介绍
flatMap()
方法首先对数组的每个元素执行一个映射函数,然后将结果压平到一个新数组中。其效果相当于先使用 map()
,然后再使用 flat()
,深度为 1。
array.flatMap(callbackFn)
array.flatMap(callbackFn, thisArg)
输入参数:
callbackFn(element, index, array)
:用于生成新数组元素的函数。thisArg
(可选):执行callbackFn
时使用的this
值。
输出:一个新的扁平化数组。
注意事项:
- 相当于先调用map(callFn, thisArg).flat(1),只是运行效率更高
- 该方法不会修改原数组,是一个复制方法。
- 对于稀疏数组,
flatMap()
会跳过空槽,因为map不会在空槽处调用函数,然后flat(1)会将第一层的空槽全部忽略。 callbackFn
应返回一个数组或值,结果将被扁平化一层。- 是通用方法
13.2 手写实现
MyArray.prototype.flatMap = function flatMap(callbackFn, thisArg) {
let arrayLike = this
// 检查 arrayLike 是否为 null 或 undefined
if (arrayLike == null) {
throw new TypeError('Cannot read property "flatMap" of ' + arrayLike);
}
// 检查 callbackFn 是否为函数
if (typeof callbackFn !== 'function') {
throw new TypeError(callbackFn + ' is not a function');
}
const O = Object(arrayLike); // 转换为对象
const len = O.length >>> 0; // 确保 length 是一个非负整数
const result = [];
for (let i = 0; i < len; i++) {
if (!(i in O)) continue; // 跳过空槽
const mappedValue = callbackFn.call(thisArg, O[i], i, O);
if (Array.isArray(mappedValue)) {
result.push(...mappedValue);
} else {
result.push(mappedValue);
}
}
return result;
}
// 测试用例
let arr = new MyArray(1, 2, 3, 4);
let result = arr.flatMap(x => [x * 2]);
console.log(result); // [2, 4, 6, 8]
let arr_2 = new MyArray().concat([1, , 3]);
let result2 = arr_2.flatMap(x => [x, x * 2]);
console.log(result2); // [1, 2, 3, 6]
let words = new MyArray("it's Sunny", "in", "", "California");
let result3 = words.flatMap(x => x.split(" "));
console.log(result3); // ["it's", "Sunny", "in", "", "California"]
难点总结:
- 结合映射和扁平化:需要在映射的同时将结果扁平化一层,通过判断数组是否是数组以及类数组对象,使用展开运算符来展开一层。
- 处理稀疏数组:空槽会被跳过,不调用
callbackFn
。 - 处理类数组对象:使用
Object()
构造函数将数组转换为对象处理
14 Array.forEach()
14.1 基本介绍
forEach()
方法对数组的每个元素执行一次提供的函数。
array.forEach(callbackFn)
array.forEach(callbackFn, thisArg)
输入参数:
callbackFn(element, index, array)
:为每个元素执行的函数。thisArg
(可选):执行callbackFn
时使用的this
值。
输出:undefined
(forEach
没有返回值)。
注意事项:
- 该方法不会修改原数组,是一个
迭代方法
。 - 对于稀疏数组,
forEach
会跳过空槽,不会调用callbackFn
。 - 无法使用
break
、return
或continue
提前终止循环,除非抛出异常。 - 期望同步函数,它不会等待Promise兑现,如果有相应的需求使用
fromAsync
函数 - 是通用方法
14.2 手写实现
MyArray.prototype.forEach = function(callbackFn, thisArg) {
if (typeof callbackFn !== "function") {
throw new TypeError(callbackFn + ' is not a function');
}
for (let i = 0; i < this.length; i++) {
if (!(i in this)) continue; // 跳过空槽
callbackFn.call(thisArg, this[i], i, this);
}
};
// 测试用例
let arr_3 = new MyArray(1, 2, 3);
arr_3.forEach((element, index) => {
console.log(`Element at index ${index} is ${element}`);
});
// Output:
// Element at index 0 is 1
// Element at index 1 is 2
// Element at index 2 is 3
let arr_4 = new MyArray().concat([1, , 3]);
arr_4.forEach((element, index) => {
console.log(`Element at index ${index} is ${element}`);
});
// Output:
// Element at index 0 is 1
// Element at index 2 is 3
难点总结:
- 处理稀疏数组:跳过空槽,不调用回调函数。
- 无法中断循环:
forEach
无法提前退出,出反日抛出错误。
15 Array.includes()
15.1 基本介绍
includes()
方法用于判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true
,否则返回 false
。
array.includes(valueToFind)
array.includes(valueToFind, fromIndex)
输入参数:
valueToFind
:需要查找的值。fromIndex
(可选):会转换为整数,开始查找的位置,默认值为0
。
输出:布尔值,表示是否包含指定的值。
注意事项:
- 使用
SameValueZero
零值相等 比较算法,NaN
被认为等于NaN
。 - 对于稀疏数组,空槽被视为
undefined
。 - 是通用方法
15.2 手写实现
MyArray.prototype.includes = function(valueToFind, fromIndex = 0) {
let length = this.length;
if (length === 0) return false;
let k = fromIndex >= 0 ? fromIndex : length + fromIndex;
if (k < 0) k = 0;
while (k < length) {
let element = this[k];
if (element === valueToFind || (Number.isNaN(element) && Number.isNaN(valueToFind))) {
return true;
}
k++;
}
return false;
};
// 测试用例
let arr_5 = new MyArray(1, 2, 3);
console.log(arr_5.includes(2)); // true
console.log(arr_5.includes(4)); // false
let arr_6 = new MyArray(1, 2, NaN);
console.log(arr_6.includes(NaN)); // true
let arr_7 = new MyArray().concat([1, , 3]);
console.log(arr_7.includes(undefined)); // true
难点总结:
-
比较算法:需要实现
SameValueZero
比较,处理NaN
的情况,其实就是使用Number.isNaN()同时判断两个数是否是NaN实现。 -
处理稀疏数组:空槽被视为
undefined
。
16 Array.indexOf()
16.1 基本介绍
indexOf()
方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回 -1
。
array.indexOf(searchElement)
array.indexOf(searchElement, fromIndex)
输入参数:
searchElement
:要查找的元素。fromIndex
(可选):开始查找的索引,默认值为0
。
输出:元素的第一个索引,如果未找到则为 -1
。
注意事项:
- 使用严格相等(
===
)比较,所以NaN永远不会被找出来。 - 对于稀疏数组,空槽会被跳过。
- 是通用方法。
16.2 手写实现
MyArray.prototype.indexOf = function(searchElement, fromIndex = 0) {
let length = this.length;
if (length === 0) return -1;
let k = fromIndex >= 0 ? fromIndex : length + fromIndex;
if (k < 0) k = 0;
for (; k < length; k++) {
if (!(k in this)) continue; // 跳过空槽
if (this[k] === searchElement) {
return k;
}
}
return -1;
};
// 测试用例
let arr_8 = new MyArray(1, 2, 3, 2);
console.log(arr_8.indexOf(2)); // 1
console.log(arr_8.indexOf(2, 2)); // 3
console.log(arr_8.indexOf(4)); // -1
let arr_9 = new MyArray().concat([1, , 3]);
console.log(arr_9.indexOf(undefined)); // -1
难点总结:
- 比较算法:使用严格相等(
===
)比较,不处理NaN
。 - 处理稀疏数组:跳过空槽,不将其视为
undefined
。
17 Array.join()
17.1 基本介绍
join()
方法将数组的所有元素连接成一个字符串,并返回这个字符串。元素是通过指定的分隔符分隔的。
array.join()
array.join(separator)
输入参数:
separator
(可选):指定一个字符串来分隔数组的每个元素,默认是逗号","
。
输出:一个由数组元素连接而成的字符串。
注意事项:
- 对于稀疏数组,空槽和
undefined
、null
会被转换为空字符串。 - 该方法不会修改原数组。
- 如果输入的数组
length
为0则返回空字符串,如果只有一个元素,直接返回该元素的字符串形式 Array.prototype.toString()
会在内部访问join
方法,不带参数。覆盖一个数组实例的join
也将覆盖它的toString
行为。
17.2 手写实现
MyArray.prototype.join = function(separator = ',') {
let result = '';
for (let i = 0; i < this.length; i++) {
if (i > 0) result += separator;
let element = this[i];
if (element == null) { // null or undefined
result += '';
} else if (typeof element === 'object' && element.toString) {
result += element.toString();
} else {
result += element;
}
}
return result;
};
// 测试用例
let arr_10 = new MyArray('Wind', 'Water', 'Fire');
console.log(arr_10.join()); // "Wind,Water,Fire"
console.log(arr_10.join(' - ')); // "Wind - Water - Fire"
let arr_11 = new MyArray().concat([1, , 3, undefined, null]);
console.log(arr_11.join()); // "1,,3,,"
难点总结:
- 处理
undefined
和null
:使用== null
来判断是否为null或undefined,将其转换为空字符串。 - 处理稀疏数组:空槽被视为
undefined
,转换为空字符串。 - 元素类型:如果元素是对象,需调用其
toString()
方法,其它元素直接加就行了,因为字符串+ 其他值会被转换为字符串。
18 Array.keys()
18.1 基本介绍
keys()
方法返回一个包含数组中每个索引键的 Array Iterator
对象。
array.keys()
输入参数:无。
输出:一个新的 Array Iterator
对象,包含数组中每个索引键。
注意事项:
- 对于稀疏数组,
keys()
也会包含空槽的索引,处理为undefined
不会忽略缺失属性的健,这和Object.keys()
的行为不同。 - 迭代器返回的顺序是数组索引的升序。
- 该方法是通用的,可用于类数组对象。
18.2 手写实现-自己实现迭代器
MyArray.prototype.keys = function() {
let index = 0;
let array = this;
// 返回迭代器对象:
// 1. 实现迭代器协议
// 2. 实现迭代器协议下的函数,返回一个还有next方法的对象,每次调用返回含有value及done属性的对象
return {
next: function(){
if(index < array.length){
const res = {value: index, done: false}
index ++
return res
}else{
return {done: true}
}
},
[Symbol.iterator]: function(){
return this
}
}
};
// 测试用例
let arr_12 = new MyArray('a', 'b', 'c');
let iterator = arr_12.keys();
console.log(iterator.next()); // { value: 0, done: false }
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
// 使用 for...of
for (let key of arr_12.keys()) {
console.log(key); // 0, 1, 2
}
let arr_13 = new MyArray().concat([1, , 3]);
for (let key of arr_13.keys()) {
console.log(key); // 0, 1, 2
18.3 使用生成器函数
MyArray.prototype.keys = function(){
const len = this.length
function* gen(){
for(let i = 0; i < len; i ++){
yield i
}
}
return gen()
}
难点总结:
- 实现迭代器协议:需要实现
next()
方法和[Symbol.iterator]()
方法。或者直接使用生成器函数 - 处理稀疏数组:索引会包含空槽的位置。
- 迭代器的可迭代性:迭代器对象本身需要是可迭代的,不能返回一个只实现了迭代器协议的对象。
19 Array.lastIndexOf()
19.1 基本介绍
lastIndexOf()
方法返回指定元素在数组中的最后一个索引,如果不存在则返回 -1
。数组从后向前搜索,从 fromIndex
处开始。
array.lastIndexOf(searchElement)
array.lastIndexOf(searchElement, fromIndex)
输入参数:
searchElement
:要查找的元素。fromIndex
(可选):开始搜索的索引,默认值为数组长度减一。
输出:元素的最后一个索引,如果未找到则为 -1
。
注意事项:
- 使用严格相等(
===
)比较。 - 对于稀疏数组,空槽会被跳过。
19.2 手写实现
MyArray.prototype.lastIndexOf = function(searchElement, fromIndex) {
let length = this.length;
if (length === 0) return -1;
let k = fromIndex !== undefined ? fromIndex : length - 1;
k = k >= 0 ? Math.min(k, length - 1) : length + k;
for (; k >= 0; k--) {
if (!(k in this)) continue; // 跳过空槽
if (this[k] === searchElement) {
return k;
}
}
return -1;
};
// 测试用例
let arr = new MyArray(2, 5, 9, 2);
console.log(arr.lastIndexOf(2)); // 3
console.log(arr.lastIndexOf(7)); // -1
console.log(arr.lastIndexOf(2, 2)); // 0
console.log(arr.lastIndexOf(2, -2)); // 0
let arr2 = new MyArray().concat([1, , 3]);
console.log(arr2.lastIndexOf(undefined)); // -1
难点总结:
- 比较算法:使用严格相等(
===
)比较。 - 处理稀疏数组:跳过空槽,不将其视为
undefined
。