面试的时候问的es6+ 有哪些部分也很多,我们可以往准备方面回答,比如数组方法就是一个秀肌肉的好地方。也别一开始就Array.of,Array.from,map什么的,人家已经听一天这种开头了。当然要答,分层面的答,不是不答,让其他先答带动这个后答,只有先答的亮了,后答才更吸引人。
会修改原数组的方法
会修改原数组的方法也被称为非纯函数,因为它们会产生副作用。慎用!!!(实用层面吸引面试官)
push/pop/shift/unshift:栈和队列操作
这些最基础方法用于数组添加或删除元素:
javascript
let arr = [1, 2, 3];
arr.push(4); // 在末尾添加元素,返回新长度:4
arr.pop(); // 删除末尾元素,返回被删除的元素:4
arr.unshift(0); // 在开头添加元素,返回新长度:4
arr.shift(); // 删除开头元素,返回被删除的元素:0
console.log(arr); // [1, 2, 3]
上面的提一嘴就行,说一说shift
和 unshift
的性能相对较差,因为它们需要移动原数组中的所有元素。
sort:排序
sort()
方法用于对数组元素进行排序,默认按字典顺序排序:
javascript
let arr = [3, 1, 2];
console.log(arr.sort(), arr); // [1, 2, 3] [1, 2, 3],原数组被修改
// 升序排序
console.log(arr.sort((a, b) => a - b));
// 降序排序
console.log(arr.sort((a, b) => b - a));
// 默认字典排序(注意数字也会被转为字符串比较)
console.log([10, 1, 20, 3, 5].sort()); // [1, 10, 20, 3, 5]
这里主要要说的就是默认排序方式不是升序,是字典排序,以utf-16为基础判断排序
splice:删除/插入/替换
splice()
用于删除、插入或替换数组元素:
javascript
const arr = [1, 2, 3, 4, 5];
const removed = arr.splice(2, 2); // 从索引2开始删除2个元素
console.log('被删除的元素:', removed); // [3, 4]
console.log('当前数组:', arr); // [1, 2, 5]
该方法会返回被删除的元素,并改变了当前数组
reverse:反转数组
reverse()
方法用于反转数组中元素的顺序:
javascript
let arr = [1, 2, 3];
console.log(arr.reverse(), arr); // [3, 2, 1] [3, 2, 1]
fill:填充数组
fill()
方法用于将数组中的元素替换为指定值:
javascript
let arr = [1, 2, 3, 4, 5];
console.log(arr.fill(0, 1, 3), arr); // [1, 0, 0, 4, 5] [1, 0, 0, 4, 5]
传入参数为传入元素,起始位置,最后位置
,瞻前不顾后
不会修改原数组的方法
这些方法不会改变原数组,而是返回新数组或新值,属于纯函数。
forEach:遍历数组
forEach()
方法用于遍历数组,无返回值:
javascript
const arr = [1, 2, 3];
arr.forEach(item => console.log(item)); // 1 2 3
forEach的遍历有个缺陷就是不能停,如果非要问你停下来的方法,当然也有,但是要先说,不推荐,如果该遍历需要停止的话,最好和最直接的方法就是用for...of方法来遍历,然后break停止。
javascript
// 报错停止
try {
[1, 2, 3, 4].forEach(num => {
if (num === 2) throw new Error("Stop");
console.log(num);
});
} catch (e) {}
// 修改数组长度
let arr = [1, 2, 3, 4];
arr.forEach((num, idx, a) => {
if (num === 2) {
a.length = idx + 1; // 直接截断数组
}
console.log(num);
});
// 强制不输出
let stop = false;
[1, 2, 3, 4].forEach(num => {
if (stop) return; // 后续全部 return,相当于不执行逻辑
if (num === 2) {
stop = true; // 标记后面的都 return
return;
}
console.log(num);
});
// 冻结数组,无法迭代了
let arr = [1, 2, 3, 4];
arr.forEach(num => {
console.log(num);
if (num === 2) Object.freeze(arr); // 让数组无法再被迭代
});
map:转换数组
map()
方法用于将数组中的每个元素转换为新元素,并返回新数组:
javascript
const arr = [1, 2, 3];
const doubled = arr.map(item => item * 2);
console.log(doubled); // [2, 4, 6]
console.log(arr); // [1, 2, 3] 原数组未改变
还有map 在 React 一个非常重要的用途,就是把一组数据映射成一组组件,从而实现数据驱动的界面展示
javascript
function UserList({ users }) {
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
为什么不用 forEach?
- forEach 没有返回值,无法直接生成一组 JSX;
- 而map 返回新数组,正好就是一组组件,非常契合 React 的渲染机制。
查找类方法
从演变角度回答,这个看情况,一题时间拉太长了也不好
indexOf/lastIndexOf(ES5)
用于查找元素在数组中的位置:
javascript
const arr = [1, 2, 3, 2];
console.log(arr.indexOf(2)); // 1,找到第一个匹配项的索引
console.log(arr.lastIndexOf(2)); // 3,找到最后一个匹配项的索引
find/findIndex(ES6)
用于查找满足条件的第一个元素或其索引:
javascript
const people = [
{name: 'Alice', age: 20},
{name: 'Bob', age: 25}
];
// 查找满足条件的第一个元素
const person = people.find(p => p.age > 20);
console.log(person); // {name: 'Bob', age: 25}
// 查找满足条件的第一个元素的索引
const index = people.findIndex(p => p.age > 20);
console.log(index); // 1
includes(ES6)
用于判断数组是否包含某个元素:
javascript
const arr = [1, 2, 3];
console.log(arr.includes(2)); // true
console.log(arr.includes(4)); // false
这个方法比较好用和常用在实际中,提一嘴。
过滤和判定类方法
filter:过滤数组
filter()
方法用于过滤出满足条件的元素,返回新数组:
javascript
const arr = [1, 2, 3, 4, 5];
const evens = arr.filter(item => item % 2 === 0);
console.log(evens); // [2, 4]
这个是有手写题的,没写过容易被拷打一遍。
javascript
Array.prototype.myFilter = function(callback, thisArg) {
if (typeof callback !== "function") {
throw new TypeError(callback + " is not a function");
}
const result = [];
const arr = this;
for (let i = 0; i < arr.length; i++) {
if (i in arr) {
if (callback.call(thisArg, arr[i], i, arr)) {
result.push(arr[i]);
}
}
}
return result;
};
every/some:判定数组
every()
和 some()
方法用于判断数组元素是否都满足或至少有一个满足某个条件:
javascript
const people = [
{name: 'Alice', age: 20, role: "user"},
{name: 'Bob', age: 25, role: "admin"},
{name: 'Charlie', age: 30, role: "user"}
];
// 检查是否所有人都成年
const allAdults = people.every(person => person.age >= 18);
console.log(allAdults); // true
// 检查是否有管理员
const hasAdmin = people.some(person => person.role === "admin");
console.log(hasAdmin); // true
拼接/裁剪类方法
slice:截取数组
slice()
方法用于截取数组的一部分,返回新数组:
javascript
const arr = [1, 2, 3, 4, 5];
const subArr = arr.slice(1, 3);
console.log(subArr); // [2, 3]
console.log(arr); // [1, 2, 3, 4, 5] 原数组未改变
concat:合并数组
concat()
方法用于合并数组:
javascript
const arr1 = [1, 2];
const arr2 = [3, 4];
const newArr = arr1.concat(arr2);
console.log(newArr); // [1, 2, 3, 4]
如果需要移除元素但不修改原数组,可以使用 slice 和 concat:
javascript
const arr = [1, 2, 3, 4, 5];
// 移除索引2开始的2个元素,但不修改原数组
const newArr = arr.slice(0, 2).concat(arr.slice(4));
console.log('新数组:', newArr); // [1, 2, 5]
console.log('原数组:', arr); // [1, 2, 3, 4, 5]
扁平化方法
flat/flatMap(ES2019)
用于将多维数组扁平化:
javascript
const arr = [1, [2, 3, [4, 5]]];
console.log(arr.flat()); // [1, 2, 3, [4, 5]] 默认只展开一层
console.log(arr.flat(2)); // [1, 2, 3, 4, 5] 指定展开2层
// flatMap = map + flat
const arr2 = [1, 2, 3];
console.log(arr2.flatMap(x => [x, x * 2])); // [1, 2, 2, 4, 3, 6]
之前看面筋有一题就和这个相关,将[1, [2, 3, [4, [5,6,[7,8,9]]]]],变成[1,2,3,4,5,6,7,8,9]
javascript
// flat 方法
const arr = [1, [2, 3, [4, [5,6,[7,8,9]]]]];
const res = arr.flat(Infinity);
console.log(res);
// [1,2,3,4,5,6,7,8,9]
// JSON.stringify 方法,有思路,但我正则写不来,不会的还是慎用
const arr = [1, [2, 3, [4, [5,6,[7,8,9]]]]];
const res = JSON.stringify(arr).replace(/\[|\]/g, '').split(',').map(Number);
console.log(res);
// [1,2,3,4,5,6,7,8,9]
迭代器方法
keys/values/entries
这些方法返回数组的迭代器:
javascript
const arr = [3, 3, 3];
// values迭代器(默认)
for(let item of arr) {
console.log(item); // 3 3 3
}
// entries迭代器,同时获取索引和值
for(let [index, item] of arr.entries()) {
console.log(index, item); // 0 3, 1 3, 2 3
}
对象不能直接用 for...of ?(因为普通对象没有实现迭代器协议),可以用keys方法判断数据内容。
join/toString:转换为字符串
javascript
const arr = [1, 2, 3];
console.log(arr.join('-')); // "1-2-3"
console.log(arr.toString()); // "1,2,3"
静态方法
Array.isArray:判断是否为数组
javascript
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
有了它就不用Object.prototype.toString.call()去判断是不是数组了,之前手写一个场景题用这个被阴阳了。
Array.from:从类数组对象创建数组
javascript
// 从字符串创建数组
console.log(Array.from('hello')); // ['h', 'e', 'l', 'l', 'o']
// 从Set创建数组
console.log(Array.from(new Set([1, 2, 2, 3]))); // [1, 2, 3]
// 从NodeList创建数组
console.log(Array.from(document.querySelectorAll('div')));
// 第二个参数可以指定映射函数
console.log(Array.from([1, 2, 3], x => x * 2)); // [2, 4, 6]
// 创建26个字母的数组
console.log(Array.from(new Array(26), (val, index) => String.fromCodePoint(index + 65)));
它也可以实现类似map的功能,但是将数组创建为数组就很别扭,人家主流功能还是将类数组对象创建为数组。
Array.of:创建数组
javascript
console.log(Array.of(1, 2, 3)); // [1, 2, 3]
// 与new Array的区别
console.log(new Array(3)); // [empty × 3]
console.log(Array.of(3)); // [3]
总结
JavaScript 数组方法丰富多样,根据是否修改原数组可分为两类:
-
会修改原数组的方法:push/pop/shift/unshift、sort、splice、reverse、fill 等
-
不会修改原数组的方法:map、filter、slice、concat、reduce 等纯函数
从优化角度回答,当然也不要为了性能优化而性能优化,把东西做出来再考虑。