发布时间:2023年6月 ES14 新增了不可变数组方法、Hashbang 语法、从后查找数组等特性。
1. 不可变数组方法
ES14 为数组新增了4个方法,它们会返回新数组而不修改原数组(函数式编程风格):
Array.prototype.toReversed()
返回反转后的新数组:
let arr = [1, 2, 3, 4, 5];
// 旧方式:reverse() 会修改原数组
let copy = [...arr].reverse(); // [5,4,3,2,1]
// 新方式:toReversed() 不修改原数组
let reversed = arr.toReversed(); // [5,4,3,2,1]
console.log(arr); // [1,2,3,4,5](原数组不变)
Array.prototype.toSorted()
返回排序后的新数组:
let arr = [3, 1, 4, 1, 5];
// 旧方式:sort() 会修改原数组
let copy = [...arr].sort((a, b) => a - b);
// 新方式:toSorted() 不修改原数组
let sorted = arr.toSorted((a, b) => a - b); // [1,1,3,4,5]
console.log(arr); // [3,1,4,1,5](原数组不变)
实际应用
// 按对象属性排序
let users = [
{ name: '张三', age: 30 },
{ name: '李四', age: 20 },
{ name: '王五', age: 25 }
];
let byAge = users.toSorted((a, b) => a.age - b.age);
// { name: '李四', age: 20 }, { name: '王五', age: 25 }, { name: '张三', age: 30 }
let byName = users.toSorted((a, b) => a.name.localeCompare(b.name));
Array.prototype.toSpliced()
返回删除/插入/替换后的新数组:
let arr = [1, 2, 3, 4, 5];
// 旧方式:splice() 会修改原数组
let copy = [...arr];
copy.splice(2, 1, 30, 40); // [1,2,30,40,4,5]
// 新方式:toSpliced() 不修改原数组
let spliced = arr.toSpliced(2, 1, 30, 40); // [1,2,30,40,4,5]
console.log(arr); // [1,2,3,4,5](原数组不变)
Array.prototype.with()
返回替换指定索引元素后的新数组:
let arr = [1, 2, 3, 4, 5];
// 旧方式
let copy = [...arr];
copy[2] = 30;
// 新方式
let updated = arr.with(2, 30); // [1,2,30,4,5]
console.log(arr); // [1,2,3,4,5](原数组不变)
// 支持负数索引
arr.with(-1, 50); // [1,2,3,4,50]
四个方法对比
| 方法 | 对应的旧方法 | 是否修改原数组 |
|---|---|---|
toReversed() | reverse() | ❌ 不修改 |
toSorted() | sort() | ❌ 不修改 |
toSpliced() | splice() | ❌ 不修改 |
with(index, value) | arr[index] = value | ❌ 不修改 |
为什么需要不可变方法?
// React 状态管理需要不可变更新
const [items, setItems] = useState([1, 2, 3]);
// 旧写法
setItems([...items].reverse());
// 新写法
setItems(items.toReversed());
// Redux 中
case 'REVERSE':
return { ...state, list: state.list.toReversed() };
2. Hashbang 语法
允许在 JS 文件的首行使用 #!(Shebang)声明解释器:
#!/usr/bin/env node
console.log('Hello from Node.js');
#!/usr/bin/env --silent deno
console.log('Hello from Deno');
使用方式
# 直接执行
chmod +x script.js
./script.js
规则
#!必须在文件的第一行- 前面不能有任何空白或其他字符
- Node.js 从 v16 开始支持
3. 从后查找数组元素
Array.prototype.findLast()
从数组末尾开始查找第一个满足条件的元素:
let arr = [1, 2, 3, 4, 5, 4, 3];
arr.find(x => x > 3); // 4(第一个大于3的)
arr.findLast(x => x > 3); // 4(最后一个大于3的)
// 找到最后一个偶数
arr.findLast(x => x % 2 === 0); // 4
// 找到某个符合条件的对象
let users = [
{ id: 1, name: '张三', active: false },
{ id: 2, name: '李四', active: true },
{ id: 3, name: '王五', active: true }
];
users.findLast(u => u.active); // { id: 3, name: '王五', active: true }
Array.prototype.findLastIndex()
从数组末尾开始查找第一个满足条件的元素索引:
let arr = [1, 2, 3, 4, 5, 4, 3];
arr.findIndex(x => x > 3); // 3(第一个大于3的索引)
arr.findLastIndex(x => x > 3); // 5(最后一个大于3的索引)
// 找不到返回 -1
arr.findLastIndex(x => x > 10); // -1
// 实际应用:从后往前找某个对象
let logs = [
{ level: 'info', msg: '启动' },
{ level: 'error', msg: '连接失败' },
{ level: 'info', msg: '重试' },
{ level: 'error', msg: '再次失败' }
];
let lastErrorIndex = logs.findLastIndex(l => l.level === 'error');
// 3
与 reverse + find 的对比
// 旧写法:需要反转再查找,或用循环
arr.slice().reverse().find(x => x > 3);
// 新写法:直接从后查找
arr.findLast(x => x > 3);
// 新写法性能更好:不需要创建新数组
4. Symbol 作为 WeakMap 的键
ES14 允许使用 Symbol 作为 WeakMap 的键(之前只能用对象):
let weakMap = new WeakMap();
let sym = Symbol('description');
weakMap.set(sym, 'value associated with symbol');
weakMap.get(sym); // 'value associated with symbol'
使用场景
// 使用 Symbol 关联私有数据
const privateData = new WeakMap();
function createClass() {
const key = Symbol('private');
return class {
constructor(value) {
privateData.set(key, value);
}
getValue() {
return privateData.get(key);
}
};
}
5. TypedArray 也支持部分 Change Array by Copy 方法
除了普通数组,toReversed()、toSorted() 和 with() 也扩展到了 TypedArray。
注意: TypedArray 不支持
toSpliced(),因为 TypedArray 的长度是固定的,无法增删元素。
let typed = new Int8Array([3, 1, 4, 1, 5]);
typed.toReversed(); // Int8Array [5, 1, 4, 1, 3]
typed.toSorted(); // Int8Array [1, 1, 3, 4, 5]
typed.with(2, 40); // Int8Array [3, 1, 40, 1, 5]
总结
| 特性 | 说明 | 重要性 |
|---|---|---|
toReversed() | 不可变反转 | ⭐⭐⭐⭐ |
toSorted() | 不可变排序 | ⭐⭐⭐⭐ |
toSpliced() | 不可变删除/插入 | ⭐⭐⭐⭐ |
with() | 不可变索引替换 | ⭐⭐⭐⭐ |
Hashbang #! | 声明脚本解释器 | ⭐⭐ |
findLast() | 从后查找元素 | ⭐⭐⭐ |
findLastIndex() | 从后查找索引 | ⭐⭐⭐ |
| Symbol 作 WeakMap 键 | 扩展 WeakMap 键类型 | ⭐⭐ |