Javascript数组研究04_手写实现_flat_flatMap_forEach_includes_indexOf_join_keys_lastIndexOf

12 Array.flat()

12.1 基本介绍

flat() 方法按照指定的深度递归遍历数组,并将所有元素与子数组中的元素合并为一个新数组返回。

array.flat(depth)

输入参数

  • depth(可选):指定要提取嵌套数组的结构深度,默认值为 1

输出:一个包含将数组元素与子数组元素合并的新数组。

注意事项

  • 该方法不会修改原数组,是一个复制方法
  • 对于稀疏数组,空槽会被跳过,不会出现在返回的新数组中。
  • 如果 depthInfinity,则会完全扁平化数组。
  • 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 值。

输出undefinedforEach 没有返回值)。

注意事项

  • 该方法不会修改原数组,是一个迭代方法
  • 对于稀疏数组,forEach 会跳过空槽,不会调用 callbackFn
  • 无法使用 breakreturncontinue 提前终止循环,除非抛出异常。
  • 期望同步函数,它不会等待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(可选):指定一个字符串来分隔数组的每个元素,默认是逗号 ","

输出:一个由数组元素连接而成的字符串。

注意事项

  • 对于稀疏数组,空槽和 undefinednull 会被转换为空字符串。
  • 该方法不会修改原数组。
  • 如果输入的数组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,,"

难点总结

  • 处理 undefinednull:使用== 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值