参考链接: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
时始终返回 -1
,includes()
则不同。如果基于 NaN === NaN
为 false
,indexOf()
的结果似乎更合理。不知 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.标记字面量
在标记字面量中,可以编写一个函数来接收字符串文字的硬编码部分,然后从该自定义函数中返回所需的任何内容。