ES7-ES11新特性
ES7新特性归纳(2016)
1、Array.prototype.includes()
判断一个数组是否包含一个元素
会首先想到indexOf(),这个方法会返回当前数组元素的下标:
const arr = ["es6", "es7", "es8", "es9", "es10", "es11"]
console.log(arr.indexOf("es6")) // 0
console.log(arr.indexOf("es12")) // -1
const arr = ["es6", "es7", "es8", "es9", "es10", "es11", NaN]
console.log(arr.indexOf(NaN)) // -1
indexOf()无法判断数组中是否含有NaN,由此,es7提供给数组一个新的API——Array.prototype.includes方法,返回一个布尔值,表示某个数组是否包含给定的值。
①基本用法
const arr = ["es6", "es7", "es8", "es9", "es10", "es11",NaN]
console.log(arr.includes("es6")) // true
console.log(arr.includes(NaN)) // true
console.log(arr.includes("es12")) // false
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
- Array.prototype.includes():可以接收两个参数,要搜索的值和搜索的开始索引。第二个参数可选,默认为
0
。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4
,但数组长度为3
),则会重置为从0
开始。 - 只能判断简单类型的数据,对于复杂类型的数据,比如对象类型的数组,二维数组,这些是无法判断的。
②区别
都可以用来判断数组中是否包含一个元素,唯一的区别在于includes可以识别NaN。
2、指数运算符
在es5中我们可以通过Math.pow()来实现:
console.log(Math.pow(2,53))
es7提供了一种 ** 运算符,可以更简单实现
console.log(2**53)
注:幂运算符的两个*号之间不能出现空格,前后有无空格都可以。
ES8新特性归纳(2017)
1、async/await
async/await是继es6中promise、generator后又一种更加优雅的异步编程的解决方案
async函数是generator函数的语法糖
在我们处理异步的时候,比起回调函数,Promise的then方法会显得较为简洁和清晰,但是在处理多个彼此之间相互依赖的请求的时候,就会显的有些繁琐。这时候,用async/await更加优雅。
使用 Promise 之后可以让我们书写异步操作更加简单,而 async 是让我们写起 Promise 像同步操作。
async用于声明一个异步的function,await用于等待一个异步方法执行完成。
基本用法
在函数,匿名函数,箭头函数,变量,类中加上关键字async。
前面添加了async的函数在执行后都会自动返回一个Promise对象:
// 不加async
function foo() {
return 'imooc'
}
console.log(foo()) // 'imooc'
// 加async
async function foo() {
return 'imooc'
}
console.log(foo()) // Promise.resolve('imooc')
async函数体内可以使用await关键字,且await关键字只能出现在async函数体内,await的作用之一就是获取后面Promise对象成功状态传递出来的参数。
function timeout() {
return new Promise(resolve => {
setTimeout(() => {
console.log(1);
resolve(); // resolve('success')
}, 1000)
})
}
// 不加async和await是2、1 加了是1、2
async function foo() {
await timeout();
console.log(2);
}
foo();
2、Object.values()、Object.entries()
获取一个对象的每一个属性值,在es5中常用Object.keys()及for in来直接获取:
// Object,keys()
const obj = {
name: "张三",
age: 18,
sex: "male",
}
const values = Object.keys(obj).map(item => {
return obj[item]
})
console.log(values) // ["张三", 18, "male"]
// for in
for (let key in obj) {
console.log(obj[key])
}
// "张三", 18, "male"
es8为我们扩展了两个新的静态方法:
①Object.values()
Object.values() 返回一个数组,数组元素是在对象上找到的可枚举属性值。
const obj = {
name: "张三",
age: 18,
sex: "male",
}
console.log(Object.values(obj)) // ["张三", 18, "male"]
②Object.entries()
Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组。
const obj = {
name: "张三",
age: 18,
sex: "male",
}
console.log(Object.entries(obj))
// [["name", "张三"],["age", "18"], ["sex", "male"]]
③区别
for in可以遍历出原型链上的可枚举属性,而Object.keys()/Object.values()/Object.entries()只能遍历自身的可枚举属性
const obj = {
name: "张三",
age: 18,
sex: "male",
}
Object.prototype.test = "test"
for (let key in obj) {
console.log(obj[key])
}
// "张三", 18, "male","test"
console.log(Object.keys(obj).map(key => obj[key]))
// ["张三", 18, "male"]
console.log(Object.values(obj))
// ["张三", 18, "male"]
console.log(Object.entries(obj).map(([key, value]) => value))
// ["张三", 18, "male"]
3、Object.getOwnPropertyDescriptors()
Object.defineProperty()获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。所谓描述符(顾名思义数据的描述符就是对数据进行描述,会描述出数据是否可以被遍历,被枚举,被赋值等行为):
- value [属性的值]
- writable [属性的值是否可被改变]
- enumerable [属性的值是否可被枚举]
- configurable [描述符本身是否可被修改,属性是否可被删除]
let test = {
name: '测试',
value: 5
}
console.log(Object.getOwnPropertyDescriptors(test))
// {
// name: {value: "测试", writable: true, enumerable: true, configurable: true}
// value: {value: 5, writable: true, enumerable: true, configurable: true}
// }
Object.getOwnPropertyDescriptors(target,param)接收两个参数,返回某一个参数的描述符。
const data = {
PortLand: '78/50',
DuLin: '88/52',
Lima: '58/40'
}
// 使指定数据不可枚举
Object.defineProperty(data, 'Lima', {
enumerable: false
})
console.log(Object.keys(data)) // ["PortLand", "DuLin"]
console.log(Object.getOwnPropertyDescriptor(data, 'Lima'))
//{value: "58/40", writable: true, enumerable: false, configurable: true}
4、String padding
ES2017 引入了字符串补全长度的功能。如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padStart(4, 'ab') // 'abax'
'x'.padEnd(5, 'ab') // 'xabab'
'x'.padEnd(4, 'ab') // 'xaba'
padStart
和padEnd
一共接受两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串。
如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串。
'xxx'.padStart(2, 'ab') // 'xxx'
'xxx'.padEnd(2, 'ab') // 'xxx'
如果用来补全的字符串与原字符串,两者的长度之和超过了指定的最小长度,则会截去超出位数的补全字符串。
'abc'.padStart(10, '0123456789')
// '0123456abc'
如果省略第二个参数,默认使用空格补全长度。
'x'.padStart(4) // ' x'
'x'.padEnd(4) // 'x '
5、尾逗号
此前,函数定义和调用时,都不允许最后一个参数后面出现逗号,es8 允许函数的最后一个参数有尾逗号。
// es8以前
function foo(a, b, c, d) {
console.log(a, b, c, d)
}
// es8
function foo(a, b, c, d,) {
console.log(a, b, c, d)
}
6、SharedArrayBuffer
SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区。
/**
*
* @param {*} length 所创建的数组缓冲区的大小,以字节(byte)为单位。
* @returns {SharedArrayBuffer} 一个大小指定的新 SharedArrayBuffer 对象。其内容被初始化为 0。
*/
const buffer = new SharedArrayBuffer(8);
console.log(buffer.byteLength);
// expected output: 8
7、Atomics对象
Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。
ES9新特性归纳(2018)
1、异步迭代
await可以和for…of循环一起使用,以串行的方式运行异步操作。
async function process(array) {
for await (let i of array) {
// doSomething(i);
}
}
2、Promise.finally()
不管promise状态如何都会执行的回调函数。
new Promise((resolve, reject) => {
resolve(1);
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
})
.finally(() => {
console.log("finally");
});
// 1
// promise
3、对象的扩展运算符
(…)运算符在数组中可以怎样使用,在对象就可以怎样使用。
const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
// 数组合并
const arr = [...arr1, ...arr2];
console.log(arr);
const obj1 = { a: 1 };
const obj2 = { b: 2 };
// 对象合并
const obj = { ...obj1, ...obj2 };
console.log(obj);
// [1, 2, 3, 4, 5, 6]
// {a: 1, b: 2}
4、正则的扩展
①dotAll/s
正则表达式中,点(.
)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用u
修饰符解决;另一个是行终止符。
所谓行终止符,就是该字符表示一行的终结。以下四个字符属于”行终止符“。
- U+000A 换行符(
\n
) - U+000D 回车符(
\r
) - U+2028 行分隔符(line separator)
- U+2029 段分隔符(paragraph separator)
ES2018 引入s
修饰符,使得.
可以匹配任意单个字符。
这被称为dotAll
模式,即点(dot)代表一切字符。所以,正则表达式还引入了一个dotAll
属性,返回一个布尔值,表示该正则表达式是否处在dotAll
模式。
const re = /foo.bar/s;
// 另一种写法
// const re = new RegExp('foo.bar', 's');
re.test('foo\nbar') // true
re.dotAll // true
console.log(/./.test(1));
console.log(/./.test("1"));
console.log(/./.test("\n"));
console.log(/./.test("\r"));
console.log(/./.test("\u{2028}"));
// true
// true
// false
// false
// false
// 使用s修饰符
console.log(/./s.test(1));
console.log(/./s.test("1"));
console.log(/./s.test("\n"));
console.log(/./s.test("\r"));
console.log(/./s.test("\u{2028}"));
// true
// true
// true
// true
// true
正则中可以使用的修饰符有i(不区分大小写),g(全局(global)匹配),m(多(more)行匹配),y(匹配必须从剩余的第一个位置开始),u(**只匹配最近的一个字符串;不重复匹配; **),s
②具名组匹配
console.log("2020-07-10".match(/(\d{4})-(\d{2})-(\d{2})/));
// ["2020-07-10", "2020", "07", "10", index: 0, input: "2020-07-10", groups: undefined]
按照 match 的语法,没有使用 g 修饰符,所以返回值第一个数值是正则表达式的完整匹配,接下来的第二个值到第四个值是分组匹配(2020, 07, 10), match 返回值还有几个属性,分别是 index、input、groups。
- index [匹配的结果的开始位置]
- input [匹配的字符串]
- groups [捕获组 ]
所谓的具名组匹配就是命名捕获分组,ES2018允许命名捕获组使用符号?,在打开捕获括号(后立即命名,这样我们就可以通过groups及命名分组获取对应的年月日的值:
console.log("2020-07-10".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/));
// groups的值
groups: {year: "2020", month: "07", day: "10"}
③反向断言
先行断言指的是,x
只有在y
前面才匹配,必须写成/x(?=y)/
反向断言,x只有在y后面才匹配,必须写成/(?<=y)x/
(?<)是后行断言的符号,配合= 、!等使用。
ES10新特性归纳(2019)
1、Array扩展
①Array.prototype.flat()
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回 。接受一个可选参数,该参数指定嵌套数组应该被展平的级别数。 如果未提供参数,则将使用默认值1:
const arr = [1, [2, [3, [4, [5, [6, 7], 8], 9]]]];
console.log(arr.flat(1));
console.log(arr.flat(5));
console.log(arr.flat(Infinity));
// [1,2,[3, [4, [5, [6, 7], 8], 9]]]
// [1,2,3,4,5,6,7,8,9]
// [1,2,3,4,5,6,7,8,9]
②Array.prototype.flatMap()
flatMap()方法返回一个新数组,不改变原数组。它与 map 和 深度值1的 flat 几乎相同。 flatMap()只能展开一层数组。
flatMap实质上包含两部分功能,一是map,二是flat。
const numbers = [1, 2, 3];
console.log(numbers.map((x) => [x ** 2]).flat());
console.log(numbers.flatMap((x) => [x ** 2]));
// [1,4,9]
// [1,4,9]
2、String.trimStart()/String.trimEnd()
作用是去掉字符串左边的空格/去掉字符串右边的空格。
const str = " hello world ";
console.log(str.trimStart());
console.log(str.trimEnd());
console.log(str.trim());
// "hello world "
// " hello world"
// "hello world"
3、Symbol.description
只读属性,返回 Symbol 对象的可选描述的字符串。
const symbol = Symbol("symbol");
console.log(symbol.description); // symbol
console.log(symbol.description === "symbol"); // true
4、Object.fromEntries()
es8中对象添加了一个entries()静态方法,这个方法返回一个给定对象自身可枚举属性的键值对数组 ,Object.fromEntries()方法与 Object.entries() 正好相反,可以将键值对列表转换为一个对象 。
const obj = {
x: 1,
y: 2,
};
const entries = Object.entries(obj);
console.log(entries);
console.log(Object.fromEntries(entries));
// [["x",1],["y":2]]
// {x:1,y:2}
只要符合entries结构的都可以使用Object.fromEntries(entries)将键值对列表转换为一个对象,比如Map:
// 通过 Object.fromEntries, 可以将 Map 转化为 Object:
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
console.log(Object.fromEntries(map)); // { foo: "bar", baz: 42 }
5、catch Building
es10允许我们在捕获异常时省略catch的参数:
// es10以前
try {
throw new Error();
} catch (error) {
console.log("fail");
}
// es10
try {
throw new Error();
} catch {
console.log("fail");
}
6、Function.prototype.toString()
Function.prototype.toString() 方法返回一个表示当前函数源代码的字符串
function test(a) {
// es10以前不返回注释部分
console.log(a);
}
console.log(test.toString());
// function test(a) {
// // es10以前不返回注释部分
// console.log(a);
// }
7、JSON扩展
JSON 内容可以支持包含 U+2028行分隔符 与 U+2029段分隔符。
在 ES10 JSON.stringify 会用转义字符的方式来处理 超出范围的 Unicode 展示错误的问题,因为 JSON 都是被编码成 UTF-8,所以遇到 0xD800–0xDFFF 之内的字符会因为无法编码成 UTF-8 进而导致显示错误。在 ES10 它会用转义字符的方式来处理这部分字符而非编码的方式,这样就会正常显示了。JSON 被归为ECMAScript的子集,在之前,JSON不是ECMAScript的子集,从而导致有些可以在JSON中包含的字符,不能够在ECMAScript的字面量中出现:
这次改变之后,我们在编码的时候就不需要再去区分是JSON还是ECMAScript了。
// \uD83D\uDE0E emoji 多字节的一个字符
console.log(JSON.stringify('\uD83D\uDE0E')) // "😎"
// 如果我们只去其中的一部分 \uD83D 这其实是个无效的字符串
// 之前的版本 ,这些字符将替换为特殊字符,而现在将未配对的代理代码点表示为JSON转义序列
console.log(JSON.stringify('\uD83D')) // "\ud83d"
ES11新特性归纳(2020)
1、空值处理
当查询某个属性时,经常会遇到,如果没有该属性就会设置一个默认的值。
const a = 0;
const b = a || 1;
console.log(b);
// 1
我们在使用||运算符时, 变量值为 0 就是 false ,所以我们会看到上述结果会输出1,但是很多时候我们希望b的输出结果就是a的值0。es11提出了空值合并运算符(??),当左侧操作数为 null 或undefined 时,返回右侧的操作数,否则返回左侧的操作数。
const a = 0;
const b = a ?? 1;
console.log(b);
// 0
2、可选链
可选链可以使我们在查询具有多层级的对象时,不再需要进行冗余的各种前置校验。
const a = {
b: {
c: {
d: {
e: "111",
},
},
},
};
// es11前
const value = a && a.b && a.b.c && a.b.c.d && a.b.c.d.e;
console.log(value);
// es11:可选链
const value2 = a?.b?.c?.d?.e;
console.log(value2);
可选链中的 ? 表示如果问号左边表达式有值, 就会继续查询问号后面的字段 ,可以大量简化类似繁琐的前置校验操作 。
3、Promise.allSettled()
返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应的promise结果。
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise4 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1,promise2,promise3, promise4]
Promise.allSettled(promiseList)
.then(values=>{
console.log(values)
});

4、import
按需加载,为了首屏渲染速度更快,很多时候都是按需加载。
定义 add.js:
export function add(a, b) {
return a + b
}
在入口文件中导入:
import { add } from './add.js' // 静态导入,不管用不用,先导入进来,相对于动态导入效率较低 console.log(add(3, 4)) // 静态引入时调用 add.js 中的 add()
// import() 方法动态导入,返回结果是一个promise,成功的值是所导入的模块 import('./add.js').then((module) => {
console.log(module.add(3, 5))
})
5、BigInt
对于js来说,最大取值范围是2的53次方:
console.log(2 ** 53);
console.log(2 ** 53 + 1);
console.log(Number.MAX_SAFE_INTEGER);
// 9007199254740992
// 9007199254740992
// 9007199254740991
es11为我们提供了第七种新的原始数据类型,BigInt,表示一个任意精度的整数,可以表示超长数据,可以超出2的53次方 。
// 声明BigInt:1、在数字后加上 n 2、用 BigInt() 将数字转为大整型
console.log(100n, typeof 100n) // 100n bigint
console.log(BigInt(100), typeof BigInt(100)) // 100n bigint
// BigInt和Number的区别,BigInt和Number不是严格相等的,但是宽松相等的。
console.log(Object.prototype.toString.call(100)) // [object Number]
console.log(Object.prototype.toString.call(100n)) // [object BigInt]
console.log(100 == 100n) // true
console.log(100 === BigInt(100)) // false
// 不能对小数进行大整数转换
console.log(1n)
// console.log(1.3n) // 报错:Uncaught SyntaxError: Invalid or unexpected token
// 运算结果带小数会被自动取整
console.log(4n / 2n) // 2n
console.log(5n / 2n) // 2n
// 一个正整数后面加上n就转换成了大整型,在数组排序中认为4n大于4,但是在逻辑上4n==4
let arr = [4, 4n, 2, 2n, -10, -10n, -1, -1n, 0, 0n]
console.log(fnSort(arr)) // [-10, -10n, -1, -1n, 0, 0n, 2, 2n, 4, 4n]
// BigInt和String的比较
console.log(2n) // 2n
console.log(2n + '') // '2'
6、globalThis
Javascript 在不同的环境获取全局对象有不通的方式:
- node 中通过 global
- web 中通过 window, self
es11提出的globalThis一句话总结就是:无论是在node环境还是web中,全局作用域中的 this 可以通过globalThis访问,不必担心它的运行环境 。
7、String.prototype.matchAll()
- matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器 ;
- 使用: str.matchAll(regexp) ;
字符串处理的一个常见场景是想要匹配出字符串中的所有目标子串:
const str =
"es2015/es6 es2016/es7 es2017/es8 es2018/es9 es2019/es10 es2020/es10";
const reg = /(es\d+)\/es(\d+)/g;
const matchs = [];
for (let match of str.matchAll(reg)) {
matchs.push(`${match[1]}-es${match[2]}`);
}
console.log(matchs);
// ["es2015-es6", "es2016-es7", "es2017-es8", "es2018-es9", "es2019-es10", "es2020-es10"]
matchAll() 是返回一个迭代器,对大数据量的场景更友好 。
8、for…in遍历机制
JavaScript 中通过for-in遍历对象时 key 的顺序是不确定的,因为规范没有明确定义,并且能够遍历原型属性让for-in的实现机制变得相当复杂,不同 JavaScript 引擎有各自根深蒂固的不同实现,很难统一。
es11不要求统一属性遍历顺序,而是对遍历过程中的一些特殊 Case 明确定义了一些规则:
- 遍历不到 Symbol 类型的属性
- 遍历过程中,目标对象的属性能被删除,忽略掉尚未遍历到却已经被删掉的属性
- 遍历过程中,如果有新增属性,不保证新的属性能被当次遍历处理到
- 属性名不会重复出现(一个属性名最多出现一次)
- 目标对象整条原型链上的属性都能遍历到