JavaScript相关知识点

发布于:2025-02-13 ⋅ 阅读:(17) ⋅ 点赞:(0)
1、数组的原生方法有哪些?哪些改变数组本身?数组的 slice()、substr()、substring() 的区别?说明一下 reduce() 参数?如何使用 reduce 实现 map?
1. 修改原数组的方法

这些方法会直接修改原数组:

  • push():在数组末尾添加一个或多个元素。

  • pop():删除并返回数组的最后一个元素。

  • shift():删除并返回数组的第一个元素。

  • unshift():在数组开头添加一个或多个元素。

  • splice():添加或删除数组中的元素。

  • sort():对数组元素进行排序。

  • reverse():反转数组元素的顺序。

  • fill():用固定值填充数组中的元素。

2. 不修改原数组的方法

这些方法不会修改原数组,而是返回一个新数组或值:

  • concat():合并数组并返回新数组。

  • slice():返回数组的浅拷贝部分。

  • join():将数组元素连接为字符串。

  • indexOf():返回指定元素的第一个索引。

  • lastIndexOf():返回指定元素的最后一个索引。

  • includes():判断数组是否包含某个元素。

  • find():返回满足条件的第一个元素。

  • findIndex():返回满足条件的第一个元素的索引。

  • filter():返回满足条件的所有元素组成的新数组。

  • map():对数组中的每个元素执行函数,返回新数组。

  • reduce():对数组中的每个元素执行累加器函数,返回最终结果。

  • reduceRight():从右到左执行 reduce()

  • some():判断是否有元素满足条件。

  • every():判断是否所有元素都满足条件。

slice()substr()substring() 的区别

这三个方法都用于截取字符串或数组的部分内容,但它们的用途和行为有所不同:

方法 适用对象 参数说明 返回值
slice() 数组/字符串 slice(start, end):从 start 开始,到 end 结束(不包括 end)。 新数组或字符串(浅拷贝)。
substr() 字符串 substr(start, length):从 start 开始,截取 length 个字符。 新字符串。
substring() 字符串 substring(start, end):从 start 开始,到 end 结束(不包括 end)。 新字符串。
const arr = [1, 2, 3, 4, 5];
const str = "Hello, World!";
console.log(arr.slice(1, 3)); // [2, 3]
console.log(str.slice(7, 12)); // "World"
console.log(str.substr(7, 5)); // "World"
console.log(str.substring(7, 12)); // "World"
reduce

reduce() 是数组的一个高阶函数,用于将数组中的每个元素依次执行累加器函数,最终返回一个累积结果。

array.reduce(callback(accumulator, currentValue, index, array), initialValue);
  1. callback:累加器函数,接收以下参数:

    • accumulator:累积值,初始值为 initialValue 或数组的第一个元素。

    • currentValue:当前处理的数组元素。

    • index(可选):当前元素的索引。

    • array(可选):原数组。

  2. initialValue(可选):初始值。如果未提供,则使用数组的第一个元素作为初始值。

const arr = [1, 2, 3, 4];
const sum = arr.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 10

使用 reduce() 实现 map()

map() 的功能是对数组中的每个元素执行函数,并返回一个新数组。我们可以用 reduce() 实现类似的功能。

function mapWithReduce(arr, callback) {
    return arr.reduce((acc, curr, index, array) => {
        acc.push(callback(curr, index, array)); // 将回调函数的结果添加到累积数组中
        return acc;
    }, []); // 初始值为空数组
}
// 示例
const arr = [1, 2, 3, 4];
const doubled = mapWithReduce(arr, (x) => x * 2);
console.log(doubled); // [2, 4, 6, 8]

实现原理

  1. 初始值:reduce() 的初始值是一个空数组 []

  2. 累加器函数每次将回调函数的结果添加到累积数组中。

  3. 返回结果:最终返回累积数组。

2、ES5/6 的继承?除了写法以外有哪些区别?

ES5 和 ES6 的继承主要区别在于实现机制和语法糖。ES5 通过原型链和构造函数模拟类的继承,而 ES6 引入了 class 关键字,使继承更加直观和简洁,支持 super 调用父类方法,提升了代码可读性和维护性。

ES5:通过构造函数和原型链实现继承,代码相对复杂

    function Parent(name) {
      this.name = name;
    }
    Parent.prototype.sayName = function () {
      console.log(this.name, "sayName");
    };
    function Child(name, age) {
      Parent.call(this, name);
      this.age = age;
    }
    Child.prototype = Object.create(Parent.prototype);
    Child.prototype.constructor = Child;
    let a = new Parent("哈哈");
    a.sayName();

 ES6:使用 classextends 关键字,语法更加简洁清晰。例如

    class Parent {
      constructor(name) {
        this.name = name;
      }
      sayName() {
        console.log(this.name);
      }
    }
    class Child extends Parent {
      constructor(name, age) {
        super(name);
        this.age = age;
      }
    }
    let a = new Parent("哈哈");
    let c = new Child("18", 12);
    a.sayName();
    console.log(c.age);

原型和构造函数的处理

ES5:需要手动处理原型链的连接,并且在子类型构造函数中通过 Parent.call(this) 来调用父类型构造函数实现属性继承,容易出现错误。

ES6extends 关键字自动处理了原型链的连接,super 关键字既可以调用父类的构造函数,也可以在子类方法中调用父类的同名方法,更加直观和安全。

子类对父类方法的重写

ES5:如果子类型要重写父类型的方法,需要直接在子类型的原型上重新定义该方法,可能会覆盖父类型原型上的其他方法,容易产生意外的副作用。

ES6:可以直接在子类中使用与父类同名的方法来实现重写,并且可以通过 super 关键字方便地调用父类的方法,避免了覆盖父类原型上其他方法的风险。

 静态方法的继承

ES5:静态方法的继承需要手动将父类的静态方法复制到子类上

ES6:子类可以直接继承父类的静态方法,无需额外的手动操作。例如:

class Parent {  
  static staticMethod() {
      console.log('Parent static method');
  }
}
class Child extends Parent {}
Child.staticMethod(); // 'Parent static method'
3、数组去重如何实现?
1、使用 Set

Set 是 ES6 引入的数据结构,它天然支持去重。

优点:代码简洁,性能较好。

缺点:无法直接处理对象类型的元素(因为对象引用不同)。

const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [...new Set(arr)];
console.log(uniqueArr); // [1, 2, 3, 4, 5]
2、使用 filter + indexOf

通过 filterindexOf 结合实现去重。

优点:兼容性好(ES5 支持)。

缺点:性能较差(indexOf 需要遍历数组)。

const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = arr.filter((item, index) => arr.indexOf(item) === index);
console.log(uniqueArr); // [1, 2, 3, 4, 5]
3、使用 reduce

通过 reduce 实现去重。

优点:灵活,适合复杂场景。

缺点:代码稍复杂。

const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = arr.reduce((acc, curr) => {
    if (!acc.includes(curr)) {
        acc.push(curr);
    }
    return acc;
}, []);
console.log(uniqueArr); // [1, 2, 3, 4, 5]
4、使用 for 循环

通过 for 循环和临时对象实现去重。

优点:性能较好。

缺点:无法直接处理对象类型的元素。

const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [];
const temp = {};
for (let i = 0; i < arr.length; i++) {
    if (!temp[arr[i]]) {
        temp[arr[i]] = true;
        uniqueArr.push(arr[i]);
    }
}
console.log(uniqueArr); // [1, 2, 3, 4, 5]
5、使用 Map

通过 Map 实现去重。

优点:性能较好,支持对象类型元素。

缺点:代码稍复杂。

const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = [];
const map = new Map();
for (const item of arr) {
    if (!map.has(item)) {
        map.set(item, true);
        uniqueArr.push(item);
    }
}
console.log(uniqueArr); // [1, 2, 3, 4, 5]
6、处理对象数组的去重

如果数组中的元素是对象,可以使用 JSON.stringify 或自定义比较函数。

优点:支持对象类型元素。

缺点:性能较差(需要遍历和比较)

 方法 1:使用 JSON.stringify

const arr = [{ id: 1 }, { id: 2 }, { id: 2 }, { id: 3 }];
const uniqueArr = arr.reduce((acc, curr) => {
    if (!acc.some(item => JSON.stringify(item) === JSON.stringify(curr))) {
        acc.push(curr);
    }
    return acc;
}, []);
console.log(uniqueArr); // [{ id: 1 }, { id: 2 }, { id: 3 }]

方法 2:自定义比较函数

const arr = [{ id: 1 }, { id: 2 }, { id: 2 }, { id: 3 }];
const uniqueArr = arr.reduce((acc, curr) => {
    if (!acc.some(item => item.id === curr.id)) {
        acc.push(curr);
    }
    return acc;
}, []);
console.log(uniqueArr); // [{ id: 1 }, { id: 2 }, { id: 3 }]
7、使用 lodash 库

如果项目中使用了 lodash 库,可以直接使用其去重方法。

优点:代码简洁,功能强大。

缺点:需要引入第三方库。 

const _ = require('lodash');
const arr = [1, 2, 2, 3, 4, 4, 5];
const uniqueArr = _.uniq(arr);
console.log(uniqueArr); // [1, 2, 3, 4, 5]