ES7在ES6的基础上主要添加了两项内容:
includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
// ex
var array = [1, 2, 3];
console.log(array.includes(2));
// expected output: true
var pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
// expected output: true
console.log(pets.includes('at'));
// expected output: false
Array.prototype.includes()方法接收两个参数:
要搜索的值
搜索的开始索引
当第二个参数被传入时,该方法会从索引处开始往后搜索(默认索引值为0)。若搜索值在数组中存在则返回true,否则返回false。 且看下面示例:
// ex:
['a', 'b', 'c', 'd'].includes('b') // true
['a', 'b', 'c', 'd'].includes('b', 1) // true
['a', 'b', 'c', 'd'].includes('b', 2) // false
和indexOf的区别,请看代码
// ex1:
var ary = [1];
if (ary.indexOf(1) !== -1) {
console.log("数组存在1")
}
if (ary.includes(1)) {
console.log("数组存在1")
}
//ex2:
var ary1 = [NaN];
console.log(ary1.indexOf(NaN))//-1
console.log(ary1.includes(NaN))//true
//ex3:
var ary1 = new Array(3);
console.log(ary1.indexOf(undefined));//-1
console.log(ary1.includes(undefined))//true
加/减法我们通常都是用其中缀形式,直观易懂。在ECMAScript2016中,我们可以使用**来替代Math.pow。
4 ** 3 // 64
// 等价于
Math.pow(4,3)
// 值得一提的是,作为中缀运算符,**还支持以下操作
let n = 4;
n **= 3;
// 64
主要新功能:
次要新功能:
Async Functions也就是我们常说的Async/Await,相信大家对于这个概念都已经不陌生了。Async/Await是一种用于处理JS异步操作的语法糖,可以帮助我们摆脱回调地狱,编写更加优雅的代码。
通俗的理解,async关键字的作用是告诉编译器对于标定的函数要区别对待。当编译器遇到标定的函数中的await关键字时,要暂时停止运行,带到await标定的函数处理完毕后,再进行相应操作。如果该函数fulfiled了,则返回值是fulfillment value,否则得到的就是reject value。
几种常见的用法,下面通过拿普通的promise写法来对比,就很好理解了:
// ex:
async function asyncFunc() {
const result = await otherAsyncFunc();
console.log(result);
}
// Equivalent to:
function asyncFunc() {
return otherAsyncFunc()
.then(result => {
console.log(result);
});
}
//按顺序处理多个异步函数的时候优势更为明显:
async function asyncFunc() {
const result1 = await otherAsyncFunc1();
console.log(result1);
const result2 = await otherAsyncFunc2();
console.log(result2);
}
// Equivalent to:
function asyncFunc() {
return otherAsyncFunc1()
.then(result1 => {
console.log(result1);
return otherAsyncFunc2();
})
.then(result2 => {
console.log(result2);
});
}
// 并行处理多个异步函数:
async function asyncFunc() {
const [result1, result2] = await Promise.all([
otherAsyncFunc1(),
otherAsyncFunc2(),
]);
console.log(result1, result2);
}
// Equivalent to:
function asyncFunc() {
return Promise.all([
otherAsyncFunc1(),
otherAsyncFunc2(),
])
.then([result1, result2] => {
console.log(result1, result2);
});
}
// 处理错误:
async function asyncFunc() {
try {
await otherAsyncFunc();
} catch (err) {
console.error(err);
}
}
// Equivalent to:
function asyncFunc() {
return otherAsyncFunc()
.catch(err => {
console.error(err);
});
}
Object.values() 方法返回一个给定对象自己的所有可枚举属性值的数组,值的顺序与使用for…in循环的顺序相同 ( 区别在于for-in循环枚举原型链中的属性 )。
obj参数是需要待操作的对象。可以是一个对象,或者一个数组(是一个带有数字下标的对象,[10,20,30] -> {0: 10,1: 20,2: 30})。
// ex:
const obj = { x: 'xxx', y: 1 };
Object.values(obj); // ['xxx', 1]
const obj = ['e', 's', '8']; // 相当于 { 0: 'e', 1: 's', 2: '8' };
Object.values(obj); // ['e', 's', '8']
// 当我们使用数字键值时,返回的是数字排序
// 根据键值排序
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']
Object.values('es8'); // ['e', 's', '8']
Object.entries 方法返回一个给定对象自身可遍历属性 [key, value] 的数组, 排序规则和 Object.values 一样。这个方法的声明比较琐碎:
// ex:
const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]
const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]
Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]
为 String 对象增加了 2 个函数:padStart 和 padEnd。
像它们名字那样,这几个函数的主要目的就是填补字符串的首部和尾部,为了使得到的结果字符串的长度能达到给定的长度。你可以通过特定的字符,或者字符串,或者默认的空格填充它。下面是函数的声明:
str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])
这些函数的第一个参数是 targetLength(目标长度),这个是结果字符串的长度。第二个参数是可选的 padString(填充字符),一个用于填充到源字符串的字符串。默认值是空格。
'es8'.padStart(2); // 'es8'
'es8'.padStart(5); // ' es8'
'es8'.padStart(6, 'woof'); // 'wooes8'
'es8'.padStart(14, 'wow'); // 'wowwowwowwoes8'
'es8'.padStart(7, '0'); // '0000es8'
'es8'.padEnd(2); // 'es8'
'es8'.padEnd(5); // 'es8 '
'es8'.padEnd(6, 'woof'); // 'es8woo'
'es8'.padEnd(14, 'wow'); // 'es8wowwowwowwo'
'es8'.padEnd(7, '6'); // 'es86666'
getOwnPropertyDescriptors 方法返回指定对象所有自身属性的描述对象。属性描述对象是直接在对象上定义的,而不是继承于对象的原型。ES2017加入这个函数的主要动机在于方便将一个对象深度拷贝给另一个对象,同时可以将getter/setter拷贝。声明如下:
Object.getOwnPropertyDescriptors(obj)
obj 是待操作对象。返回的描述对象键值有:configurable, enumerable, writable, get, set and value。
const obj = {
get es7() { return 777; },
get es8() { return 888; }
};
Object.getOwnPropertyDescriptors(obj);
// {
// es7: {
// configurable: true,
// enumerable: true,
// get: function es7(){}, //the getter function
// set: undefined
// },
// es8: {
// configurable: true,
// enumerable: true,
// get: function es8(){}, //the getter function
// set: undefined
// }
// }
结尾逗号用代码展示非常明了:
// 参数定义时
function foo(
param1,
param2,
) {}
// 函数调用时
foo(
'abc',
'def',
);
// 对象中
let obj = {
first: 'Jane',
last: 'Doe',
};
// 数组中
let arr = [
'red',
'green',
'blue',
];
这个改动有什么好处呢?
[
'foo'
]
修改为
[
'foo',
'bar'
]
导致线条’foo’和线条’bar’被标记为已更改,即使唯一真正的变化是后一条线被添加。
主要新功能:
新的正则表达式功能:
其他新功能:
ES6引入了同步迭代器,其工作原理如下:
示例:
const iterable = ['a', 'b'];
const iterator = iterable[Symbol.iterator]();
iterator.next()
// { value: 'a', done: false }
iterator.next()
// { value: 'b', done: false }
iterator.next()
// { value: undefined, done: true }
先前的迭代方式是同步的,并不适用于异步数据源。例如,在以下代码中,readLinesFromFile()无法通过同步迭代传递其异步数据:
function readLinesFromFile (){
setTimeout(() => {
return "123456"
},1000)
}
for (const line of readLinesFromFile()) {
console.log(line);
}
function *createNumberIterator(){
yield 1;
yield 2;
yield 3;
}
const iterator = createNumberIterator();
const p2 = iterator.next(); // Object {value: 2, done: false}
const p3 = iterator.next(); // Object {value: 3, done: false}
const p4 = iterator.next(); // Object {value: undefined, done: true}
const p1 = iterator.next(); // Object {value: 1, done: false}
log(p1, p2, p3, p4)
异步迭代器和常规迭代器的工作方式非常相似,但是异步迭代器涉及promise:
async function example() {
// 普通迭代器:
const iterator = createNumberIterator();
iterator.next(); // Object {value: 1, done: false}
iterator.next(); // Object {value: 2, done: false}
iterator.next(); // Object {value: 3, done: false}
iterator.next(); // Object {value: undefined, done: true}
// 异步迭代器:
const asyncIterator = createAsyncNumberIterator();
const p = asyncIterator.next(); // Promise
await p;// Object {value: 1, done: false}
await asyncIterator.next(); // Object {value: 2, done: false}
await asyncIterator.next(); // Object {value: 3, done: false}
await asyncIterator.next(); // Object {value: undefined, done: true}
}
异步迭代器对象的next()方法返回了一个Promise,解析后的值跟普通的迭代器类似。
用法:iterator.next().then(({ value, done })=> {//{value: ‘some val’, done: false}}
const promises = [
new Promise(resolve => resolve(1)),
new Promise(resolve => resolve(2)),
new Promise(resolve => resolve(3)),
];
async function test() {
for await (const p of promises) {
console.log(p);
}
}
test(); //1 ,2 3
这个就是我们通常所说的rest参数和扩展运算符,这项特性在ES6中已经引入,但是ES6中的作用对象仅限于数组
restParam(1, 2, 3, 4, 5);
function restParam(p1, p2, ...p3) {
// p1 = 1
// p2 = 2
// p3 = [3, 4, 5]
}
const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100
在ES9中,为对象提供了像数组一样的rest参数和扩展运算符:
const obj = {
a: 1,
b: 2,
c: 3
}
const { a, ...param } = obj;
console.log(a) //1
console.log(param) //{b: 2, c: 3}
function foo({a, ...param}) {
console.log(a); //1
console.log(param) //{b: 2, c: 3}
}
//正则表达式命名捕获组
const RE_DATE = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31
通过数字引用捕获组有几个缺点:
ES9中可以通过名称来识别捕获组:(?
在这里,我们用名称标记了前一个捕获组year。该名称必须是合法的JavaScript标识符(认为变量名称或属性名称)。匹配后,您可以通过访问捕获的字符串matchObj.groups.year来访问。
让我们重写前面的代码:
const RE_DATE = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/;
const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31
// 使用解构语法更为简便
const {groups: {day, year}} = RE_DATE.exec('1999-12-31');
console.log(year); // 1999
console.log(day); // 31
可以发现,命名捕获组有以下优点:
该特性允许您使用\p{}通过提及大括号内的Unicode字符属性来匹配字符,在正则表达式中使用标记 u (unicode) 设置。
/^\p{White_Space}+$/u.test('\t \n\r')
// true
/^\p{Script=Greek}+$/u.test('μετά')
// true
由于在Unicode里面,中文字符对应的Unicode Script是Han,于是我们就可以用这个reg来匹配中文:
/\p{Script=Han}/u
这样我们就可以不用记忆繁琐又不好记的/[\u4e00-\u9fa5]/了,况且这个表达式已经有些年头了,说实话,后来又新增的属性为Han的字符并不在这个范围内,因此这个有年头reg并不一定好使。
我随便从网上找了一个Unicode8.0添加的中文字符“?”,我测了一下两种reg的兼容性:
oldReg=/[\u4e00-\u9fa5]/
newReg=/\p{Script=Han}/u
oldReg.test('abc')
// false
newReg.test('abc')
// false
oldReg.test('地平线')
// true
newReg.test('地平线')
// true
oldReg.test('?')
// false
newReg.test('?')
// true
先来看下正则表达式先行断言是什么:
如获取货币的符号
const noReLookahead = /\D(\d+)/,
reLookahead = /\D(?=\d+)/,
match1 = noReLookahead.exec('$123.45'),
match2 = reLookahead.exec('$123.45');
console.log(match1[0]); // $123
console.log(match2[0]); // $
在ES9中可以允许反向断言:
const reLookahead = /(?<=\D)[\d\.]+/;
match = reLookahead.exec('$123.45');
console.log(match[0]); // 123.45
使用?<=
进行反向断言,可以使用反向断言获取货币的价格,而忽略货币符号。
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
finally的回调总会被执行。注意.finally()执行之后便在.then 或者.catch的话是不会执行的
ES2018 移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制。
之前,\u开始一个 unicode 转义,\x开始一个十六进制转义,\后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:\uuu\xxx\111。
要取消转义序列的语法限制,可在模板字符串之前使用标记函数String.raw:
`\u{54}`
// "T"
String.raw`\u{54}`
// "\u{54}"