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);
callback:累加器函数,接收以下参数:
accumulator:累积值,初始值为
initialValue
或数组的第一个元素。currentValue:当前处理的数组元素。
index(可选):当前元素的索引。
array(可选):原数组。
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]
实现原理
初始值:
reduce()
的初始值是一个空数组[]
。累加器函数:每次将回调函数的结果添加到累积数组中。
返回结果:最终返回累积数组。
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:使用 class
和 extends
关键字,语法更加简洁清晰。例如
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)
来调用父类型构造函数实现属性继承,容易出现错误。ES6:
extends
关键字自动处理了原型链的连接,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
通过
filter
和indexOf
结合实现去重。优点:兼容性好(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]