ES8(ES2017)新特性

发布时间:2017年6月 ES8 新增了异步编程的关键特性,同时完善了字符串、对象等基础能力。


1. async/await

ES8 最重要的特性,让异步代码看起来像同步代码。

基本语法

js 复制代码
async function fetchData() {
  let res = await fetch('/api/data');
  let data = await res.json();
  return data;
}

工作原理

  • async 函数总是返回一个 Promise
  • await 只能在 async 函数内使用
  • await 暂停函数执行,等待 Promise 结果

对比回调地狱和 Promise 链

js 复制代码
// 回调地狱
getData(function(a) {
  getMoreData(a, function(b) {
    getEvenMoreData(b, function(c) {
      console.log(c);
    });
  });
});

// Promise 链
getData()
  .then(a => getMoreData(a))
  .then(b => getEvenMoreData(b))
  .then(c => console.log(c))
  .catch(err => console.error(err));

// async/await,最清晰
async function getAll() {
  let a = await getData();
  let b = await getMoreData(a);
  let c = await getEvenMoreData(b);
  console.log(c);
}

错误处理

js 复制代码
// try...catch 方式
async function fetchData() {
  try {
    let res = await fetch('/api/data');
    let data = await res.json();
    return data;
  } catch (err) {
    console.error('请求失败:', err);
  }
}

// 也可以用 .catch()
fetchData().catch(err => console.error(err));

并行执行多个异步操作

js 复制代码
// 串行执行(慢)
async function serial() {
  let a = await fetch('/api/a');
  let b = await fetch('/api/b');
  return [a, b];
}

// 并行执行(快)
async function parallel() {
  let [a, b] = await Promise.all([
    fetch('/api/a'),
    fetch('/api/b')
  ]);
  return [a, b];
}

立即执行

js 复制代码
(async function() {
  let data = await fetchData();
  console.log(data);
})();

注意事项

  • await 只能等待 Promise,非 Promise 值会自动包装
  • 在循环中慎用 await(会导致串行执行)
  • 顶层 await 需要 ES2022(模块环境下)

2. Object.values()

返回对象自身所有可枚举属性值的数组:

js 复制代码
let obj = { a: 1, b: 2, c: 3 };
Object.values(obj);  // [1, 2, 3]

使用场景

js 复制代码
// 获取所有属性值
let scores = { math: 90, english: 85, science: 92 };
let values = Object.values(scores);  // [90, 85, 92]

// 计算平均值
let avg = values.reduce((a, b) => a + b) / values.length;  // 89

// 检查是否有某个值
Object.values(obj).includes('target');

注意

  • 只返回自身的可枚举属性,不包括继承的
  • 属性顺序与 for...in 一致
  • 字符串对象也能用
js 复制代码
Object.values('hello');  // ['h', 'e', 'l', 'l', 'o']

3. Object.entries()

返回对象自身可枚举属性的键值对数组:

js 复制代码
let obj = { a: 1, b: 2, c: 3 };
Object.entries(obj);  // [['a', 1], ['b', 2], ['c', 3]]

配合 for...of 遍历

js 复制代码
for (let [key, value] of Object.entries(obj)) {
  console.log(key, value);
}

将对象转为 Map

js 复制代码
let map = new Map(Object.entries(obj));

使用场景

js 复制代码
// 过滤对象属性
let filtered = Object.fromEntries(
  Object.entries(obj).filter(([k, v]) => v > 1)
);

// 映射对象值
let mapped = Object.fromEntries(
  Object.entries(obj).map(([k, v]) => [k, v * 2])
);

注意

  • Object.fromEntries() 是 ES10 才有的,ES8 只有 Object.entries()
  • 字符串对象也能用
js 复制代码
Object.entries('ab');  // [['0', 'a'], ['1', 'b']]

4. String.prototype.padStart()

字符串头部补全:

js 复制代码
'5'.padStart(2, '0');      // '05',补到2位
'5'.padStart(4, '0');      // '0005',补到4位
'abc'.padStart(5, 'xy');   // 'xyabc',从左补
'abc'.padStart(2);         // 'abc',超过长度不截断

语法

js 复制代码
str.padStart(targetLength[, padString])

实际应用

js 复制代码
// 日期格式化
let month = '5'.padStart(2, '0');   // '05'
let day = '9'.padStart(2, '0');     // '09'

// 序号格式化
'1'.padStart(3, '0');   // '001'
'42'.padStart(3, '0');  // '042'

// 卡号隐藏
'1234567890'.padStart(14, '*');  // '****1234567890'

5. String.prototype.padEnd()

字符串尾部补全:

js 复制代码
'5'.padEnd(2, '0');      // '50'
'abc'.padEnd(5, '.');    // 'abc..'
'abc'.padEnd(5, 'xy');   // 'abcxy'
'abc'.padEnd(2);         // 'abc',超过长度不截断

实际应用

js 复制代码
// 对齐输出
console.log('姓名'.padEnd(10, ' ') + '分数');
console.log('张三'.padEnd(10, ' ') + '90');
console.log('李四'.padEnd(10, ' ') + '85');

// 输出:
// 姓名       分数
// 张三       90
// 李四       85

注意

  • 如果补全字符串长度超过目标长度,会截断补全字符串
js 复制代码
'abc'.padStart(6, '123456');  // '123abc',截断为'123'
'abc'.padEnd(6, '123456');    // 'abc123'

6. Object.getOwnPropertyDescriptors()

获取对象自身所有属性的描述符:

js 复制代码
let obj = {
  name: '张三',
  get age() { return 18; }
};

Object.getOwnPropertyDescriptors(obj);
// {
//   name: { value: '张三', writable: true, enumerable: true, configurable: true },
//   age:  { get: [Function], set: undefined, enumerable: true, configurable: true }
// }

对比 getOwnPropertyDescriptor(单数)

js 复制代码
// 单数:获取单个属性描述符(ES5)
Object.getOwnPropertyDescriptor(obj, 'name');

// 复数:获取所有属性描述符(ES8)
Object.getOwnPropertyDescriptors(obj);

实际用途:深拷贝 + 正确拷贝 getter/setter

js 复制代码
// Object.assign 会丢失 getter/setter
let source = {
  get foo() { return 1; }
};
let copy = Object.assign({}, source);
// copy.foo = 1,变成普通值,不是 getter 了

// 正确的拷贝方式
let properCopy = Object.defineProperties(
  {},
  Object.getOwnPropertyDescriptors(source)
);
// properCopy.foo 是 getter,能正常工作

7. 函数参数末尾允许逗号

ES8 新增的是:函数定义的参数列表函数调用的参数列表也允许写尾随逗号。对象字面量和数组字面量更早以前就已经支持尾随逗号了。

js 复制代码
// ES8 之前,对象和数组里就已经能写尾随逗号
let obj = {
  a: 1,
  b: 2,
};

let arr = [
  1,
  2,
];

// ES8 进一步允许函数参数列表写尾随逗号
function foo(
  a,
  b,
  c,
) {}

// 函数调用时也允许
foo(
  1,
  2,
  3,
);

好处

  • 添加新参数或重排参数时,git diff 更干净
  • 修改最后一项时不需要额外补逗号
  • 末尾逗号在语义上会被忽略,不影响执行

8. SharedArrayBuffer 和 Atomics

用于多线程编程。

SharedArrayBuffer

允许多个 Web Worker 共享同一块内存:

js 复制代码
let sab = new SharedArrayBuffer(1024);  // 1KB 共享内存
let arr = new Int32Array(sab);
arr[0] = 42;

Atomics

提供原子操作,防止竞态条件:

js 复制代码
let sab = new SharedArrayBuffer(4);
let arr = new Int32Array(sab);

// 在 Worker 中
Atomics.add(arr, 0, 5);    // 原子加5
Atomics.store(arr, 0, 10); // 原子写入
Atomics.load(arr, 0);      // 原子读取
Atomics.compareExchange(arr, 0, 10, 20); // 原子比较并交换
Atomics.wait(arr, 0, 10);  // 等待值变为10
Atomics.notify(arr, 0, 1); // 通知等待者

注意:由于安全原因(Spectre 漏洞),主流浏览器曾短暂禁用 SharedArrayBuffer,现在需要跨域隔离(Cross-Origin Isolation)才能使用。


总结

特性 说明 重要性
async/await 异步编程语法糖 ⭐⭐⭐⭐⭐ 最重要的特性
Object.values() 获取对象所有属性值 ⭐⭐⭐
Object.entries() 获取对象键值对数组 ⭐⭐⭐
String.padStart() 字符串头部补全 ⭐⭐⭐
String.padEnd() 字符串尾部补全 ⭐⭐⭐
Object.getOwnPropertyDescriptors() 获取所有属性描述符 ⭐⭐
函数参数末尾逗号 函数参数末尾允许逗号 ⭐⭐
SharedArrayBuffer/Atomics 共享内存和原子操作 ⭐(特殊场景)
相关推荐
kyriewen10 小时前
别再 console.log 了:5 个 Chrome DevTools 调试技巧,用过就回不去了
前端·javascript·面试
IT_陈寒12 小时前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
To_OC12 小时前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
DigitalOcean13 小时前
Laravel 开发者已在 DigitalOcean 上开通超过 10 万台服务器
前端·laravel
星始流年14 小时前
从 Tool 到 Skill——基于 LangChain 的服务端Skill实现
前端·langchain·agent
李惟14 小时前
开源本地通信库,纯客户端 RPC,像聊天一样通信
前端
YAwu1114 小时前
深入解析 React 炫彩鼠标跟随标题组件:从坐标定位到动画性能
前端·react.js
GuWenyue14 小时前
排序效率低?5分钟吃透快速排序,性能飙升至O(nlogn)
前端·javascript·面试
OpenTiny社区14 小时前
🎨 看完 GenUI SDK 源码我悟了!
前端·vue.js·github
叁两14 小时前
前端转型AI Agent该如何学习?(前置篇)
前端·人工智能·node.js