ECMAScript 2016、2017、2018 新 API

本文回顾了ECMAScript从ES6到ES2018的发展,详细介绍了各版本中新增的重要功能,如Array.prototype.includes()、乘方中缀操作符、Object.values()、Object.entries()、字符串填充、尾随逗号、Async/Await等,并解释了这些新特性如何改善JavaScript编程体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接:https://csspod.com/new-in-ecmascript-2016-2017-and-2018/

                   https://www.cnblogs.com/Merrys/p/8875662.html

回顾一下 ECMAScript 的发展历程:

  • 1997 年 6 月,ES1 发布
  • 1998 年 6 月,ES2 发布
  • 1999 年 12 月,ES3 发布
  • 然后,ES4 胎死腹中
  • 2009 年 12 月,ES5 发布
  • 2015 年 6 月,ES6(ES2015)发布
  • ES2016
  • ES2017
  • ES2018
  • ……

经过 ES3 到 ES5 10 年的停滞以后,从 ES6 (ES2015) 开始,又开始小跑向前,连版本命名都改为年份的方式。

ECMAScript 2016

1、Array.prototype.includes()

Array.property.includes() indexOf()不支持查找NaN,includes支持。

onst arr = [1, 2, 3, NaN];

arr.includes(3); // true
arr.includes(NaN); // true

arr.indexOf(NaN); // -1

注意indexOf() 查找 NaN 时始终返回 -1includes() 则不同。如果基于 NaN === NaN 为 falseindexOf() 的结果似乎更合理。不知 includes() 为何没有遵循这个逻辑。

2、乘方中缀操作符

7**2 指数运算符,结果为49

ES 中已经有 ++-- 操作符,ES2016 引入了 ** 操作符进行乘方操作,作为 Math.pow 的一种替代。

Math.pow(7, 2); // 49

7 ** 2 // 49

ECMAScript 2017

1、Object.values()

Object.values() 返回Object自身属性的所有值,排除原型链中的所有值。

Object.keys() 返回一个对象的 key 数组,Object.values() 则返回值的数组。

const cars = { BENZ: 3, Tesla:2, Honda: 1 };

// ES2015
const carVals = Object.keys(cars).map(key => cars[key]);
// [3, 2, 1]

// ES2017
const vals = Object.values(cars);

2、Object.entries()

Object.entries() 以二维数组的形式返回keys和values。

const cars = { BENZ: 3, Tesla:2, Honda: 1 };

// ES 5.1
Object.keys(cars).forEach(key => {
  console.log(`key: ${key} value: ${cars[key]}`);
});

// ES2017
for (let [key, value] of Object.entries(cars)) {
  console.log(`key: ${key} value: ${value}`);
}

// ES2015
const map1 = new Map();
Object.keys(cars).forEach(key => {
  map1.set(key, cars[key]);
});

// ES2017
const map = new Map(Object.entries(cars));

3.String Padding字符串填充

向字符串String.proptotype.padStart和String.prototype.padEnd添加了两个实例方法--他们允许在原始字符串的开始或是结尾附加、预先添加空字符串或是其他字符串。

  •  String.prototype.padStart(numberOfCharcters [,stringForPadding]);
  • String.prototype.padEnd(numberOfCharcters [,stringForPadding]);

前置/后置填充,第一个参数为字符串的目标长度,第二个参数为填充的字符,默认为空格。如果目标长度小于字符串的长度,则不做处理,直接返回原始字符串。

字符串填充一般用于界面中对齐文字,美化展示效果。

'5'.padStart(10) // '          5'
'5'.padStart(10, '=*') //'=*=*=*=*=5'
'5'.padEnd(10) // '5         '
'5'.padEnd(10, '=*') //'5=*=*=*=*='
//ES2017
//如果你有一个不同长度的项目列表,并希望格式化它们的显示目的,你可以使用padStart
const formatted = [0, 1, 12, 123, 1234, 12345].map(num => 
    num.toString().padStart(10, '0') // 添加 0 直到长度为 10
);
console.log(formatted);
//打印
// [
//     '0000000000',
//     '0000000001',
//     '0000000012',
//     '0000000123',
//     '0000001234',
//     '0000012345',
// ]
const cars = {
  '?BMW': '10',
  '?Tesla': '5',
  '?Lamborghini': '0'
}
Object.entries(cars).map(([name, count]) => {
  //padEnd appends ' -' until the name becomes 20 characters
  //padStart prepends '0' until the count becomes 3 characters.
  console.log(`${name.padEnd(20, ' -')} Count: ${count.padStart(3, '0')}`)
});
//打印结果..
// ?BMW - - - - - - -  Count: 010
// ?Tesla - - - - - -  Count: 005
// ?Lamborghini - - -  Count: 000

 

Emoji 等双字节字符填充

Emoji 等使用多字节 unicode 呈现,padStart 和 padEnd 可能无法得到预期的结果。

看下面的例子:

'heart'.padStart(10, "❤️");
22:10:05.595 "❤️❤️❤heart"

 上面的代码执行结果是 2 个 ❤️ 和 1 个 ❤,而不是五个 ️❤️。这是因为 ❤️ 码点长度为 2 ('\u2764\uFE0F'),heart 的长度为 5,总共只能填充 5 个字符,也就是 '\u2764\uFE0F\u2764\uFE0F\u2764'

4.Object.getOwnPropertyDescriptors

此方法返回给定对象的所有属性的所有详细信息(包括 get``set 方法). 添加这个的主要动机是允许浅拷贝/克隆一个对象到另一个对象中,这个对象也拷贝getter和setter函数,

Object.assign。Object.assign浅克隆除原始源对象的getter和setter函数以外的所有详细信息。Object.getOwnPropertyDescriptors 的主要目的就是解决这一问题。

var Car = {
    name: 'BMW',
    price: 1000000,
    set discount(x) {
        this.d = x;
    },
    get discount() {
        return this.d;
    },
};
//使用Object.defineProperties将Car的属性复制到ElectricCar2
//并使用Object.getOwnPropertyDescriptors提取Car的属性
const ElectricCar2 = Object.defineProperties({}, Object.getOwnPropertyDescriptors(Car));
//打印ElectricCar2对象“discount”属性的详细信息
console.log(Object.getOwnPropertyDescriptor(ElectricCar2, 'discount'));
//prints..
// { get: [Function: get],
//   set: [Function: set], 
//   enumerable: true,
//   configurable: true 
// }
// 请注意,getter和setter存在于ElectricCar2对象中,用于'discount'属性!

5.尾随逗号

函数参数尾部逗号主要是解决 git blame 等工具使用问题

// 问题

// 开发者 #1 创建了函数
function Person(
  name,
  age //<-- ES2017 以前的版本添加参数尾部逗号会抛出错误
) {
  this.name = name;
  this.age = age;
}

// 开发者 #2 填加了一个参数
function Person(
  name,
  age, // 添加了逗号 <--- 由于这个逗号,开发者 #1 会被 git blame 等工具标记为修改者
  address
) {
  this.name = name;
  this.age = age;
  this.address = address;
}

// ES2017 支持参数尾部逗号,解决这一问题
function Person(
  name,
  age, //<--- 不会报错,开发者 #2 不需要再修改此行
) {
  // ...
}

函数调用时添加尾部逗号也是允许的。

Math.max(10, 20,);

6.Async/Await

这应该是最重要、最有用的功能了。先是 callback 噩梦,然后有了 Promise 链式写法,一路跌跌撞撞,总算有了更清晰明了的 Async/Await。

async 关键字声明一个异步函数,JS 编译器每次执行到包含 await 的语句时都会暂定,等待 await 后面的语句返回的 Promise resolve 或 reject 后才继续往下执行。

// ES2015 Promise
function getAmount(userId) {
  getUset(userId)
    .then(getBankBabance)
    .then(amount => {
      console.log(amount);
    });
}

// ES2017
async function getAmount7(userId) {
  const user = await getUser(userId);
  const amount = getBankBalance(user);
  
  console.log(amount);
}

getAmount('1'); // $1000
getAmount7('1'); // $1000

function getUser(userId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('john');
    }, 1000);
  });
}

function getBankBalance(user) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (user === 'john') {
        resolve('$1000');
      } else {
        reject('unknown user');
      }
    }, 1000);
  });
}

Async 函数本身返回 Promise

Async 函数的返回结果需要使用 Promise 的 .then 方法处理。

function getUser(userId) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('john');
    }, 1000);
  });
}

async function getUserName(userId) {
  const name = await getUser(userId);
  // 其他操作
  // ...

  return name;
}

getUserName(userId)
  .then(console.log);

并行调用

可以使用 Promise.all 并行调用 await 后面的语句。

function doubleAfter1Sec(param) {
  return new Promise(resolve => {
    setTimeout(resolve(param * 2), 1000);
  });
}

async function doubleAndAdd(a, b) {
  [a, b] = await Promise.all([doubleAfter1Sec(a), doubleAfter1Sec(b)]);
  
  return a + b;
}

doubleAndAdd(1, 2)
  .then(console.log); // 6

错误处理

方式 1:在函数内部使用 try catch

// Option 1 - Use try catch within the function
async function doubleAndAdd(a, b) {
  try {
    a = await doubleAfter1Sec(a);
    b = await doubleAfter1Sec(b);
  } catch (e) {
    return NaN; // return something
  }
  return a + b;
}

// ?Usage:
doubleAndAdd('one', 2).then(console.log); // NaN
doubleAndAdd(1, 2).then(console.log); // 6

function doubleAfter1Sec(param) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      let val = param * 2;
      isNaN(val) ? reject(NaN) : resolve(val);
    }, 1000);
  });
}

方式 2:捕获每个 await 表达式

//Option 2 - *Catch* errors on  every await line
//as each await expression is a Promise in itself
async function doubleAndAdd(a, b) {
  a = await doubleAfter1Sec(a).catch(e => console.log('"a" is NaN')); // ?
  b = await doubleAfter1Sec(b).catch(e => console.log('"b" is NaN')); // ?

  if (!a || !b) {
    return NaN;
  }

  return a + b;
}

// Usage:
doubleAndAdd('one', 2).then(console.log); // NaN  and logs:  "a" is NaN
doubleAndAdd(1, 2).then(console.log); // 6

function doubleAfter1Sec(param) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      let val = param * 2;
      isNaN(val) ? reject(NaN) : resolve(val);
    }, 1000);
  });
}

方式 3:捕获整个 async-await 函数

// Option 3 - Dont do anything but handle outside the function
// since async / await returns a promise, we can catch the whole function's error
async function doubleAndAdd(a, b) {
  a = await doubleAfter1Sec(a);
  b = await doubleAfter1Sec(b);
  return a + b;
}

// Usage:
doubleAndAdd('one', 2)
  .then(console.log)
  .catch(console.log); // ?? <------- use "catch"

function doubleAfter1Sec(param) {
  return new Promise((resolve, reject) => {
    setTimeout(function() {
      let val = param * 2;
      isNaN(val) ? reject(NaN) : resolve(val);
    }, 1000);
  });
}

ECMAScript 2018

1.共享内存和原子

2.标记字面量

在标记字面量中,可以编写一个函数来接收字符串文字的硬编码部分,然后从该自定义函数中返回所需的任何内容。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值