一、es7 新特性
1、Array.prototype.includes()
方法
该方法可以返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似。
[1,2,3].includes(4) // false
['ss', 'aa'].includes('aa') // true
该方法接受2个参数,第二个参数表示搜索的起始位置,默认为0。如果参数为负数,则表示倒数。
[1, 2, 3].includes(3, 3) // false
[1, 2, 3].includes(3, -1) // true
我们通常使用indexOf
来检查是否包含某个元素,但是会有缺点:一是不够直观,要和-1去比较;二是它内部使用严格相等(===)来判断,导致会对NaN
的误判。
[NaN].indexOf(NaN) // -1
[NaN].includes(NaN) // true
浏览器支持情况
目前只有IE不支持。
2、求幂运算符**
**
具有与Math.pow()
同样的计算效果。
console.log(2**10) // 1024
console.log(Math.pow(2, 10)) // 1024
浏览器支持情况
目前只有IE浏览器不支持。
二、es8 新特性
1、async/await
异步解决方案
提出场景:JS 是单线程、优化回调地狱的写法。
es6
为了解决回调的方式,提出了promise
的then
函数,但是当业务逻辑过多时,需要多个then
函数,此时语义上不是很清晰。
new Promise( (resolve, reject) => {
this.login(resolve)
}).then(() => this.getInfo())
.then(() => {// do something})
.catch(() => { console.log("Error") })
基于上述,引入了async/await
,提供了在不阻塞主线程的情况下使用同步代码来实现异步访问资源的能力,其中await
不可以脱离async
单独使用,且await
后面一定是Promise
对象,如果不是会自动包装成Promise
对象。
async function getInfo() {
const result1 = await this.login()
const result2 = await this.getInfo()
}
而async
可以单独使用,且返回的是一个Promise
对象,且其内部return
语句的返回值,会成为then
方法回调函数的参数。
async function f() {
return 'hello world';
}
f().then(val => console.log(val)) // hello world
具体关于async
的用法,可参考 es6.ruanyifeng.com/#docs/async
浏览器支持情况
2、Object.values()/Object.entries()
方法
Object.values()
方法会返回一个数组,其数组成员是参数对象自身可枚举属性的键值。
注意:当属性值是数值时,会按照数值大小 从小到大遍历。
const obj1 = { foo: 'bar', baz: 42 }
Object.values(obj1) // ["bar", 42]
const obj2 = { 100: 'a', 1: 'b', 2: 'c' }
Object.values(obj2) // ["b", "c", "a"]
Object.entries()
方法返回一个数组,数组成员是参数对象自身可枚举属性的键值对数组。
const obj1 = { foo: 'bar', baz: 42 }
Object.entries(obj1) // [ ["foo, "bar"], ["baz", 42] ]
const obj2 = { 100: 'a', 1: 'b', 2: 'c' }
Object.entries(obj2) // [ ["1", "b"], ["2", "c"], ["100", "a"] ]
浏览器支持情况
目前只有IE浏览器不支持。
3、字符串填充padStart()/padEnd()
方法
用于字符串补全长度的功能,如果某个字符串不够指定长度,会在头部或尾部补全。padStart()
用于头部补全,padEnd()
用于尾部补全。
padStart()/padEnd()
方法接收2个参数,第一个参数是字符串补全生效的最大长度,第二个参数是用来补全的字符串。
'x'.padStart(5, 'ab') // 'ababx'
'x'.padEnd(5, 'ab') // 'xabab'
'xxx'.padStart(2, 'ab') // 'xxx'
如果用来补全的字符串与原字符串,两者的长度之和超过了最大长度,则会截去超出位数的补全字符串。
'abc'.padStart(8, 'xxx') // 'xxxxxabc'
浏览器支持情况
目前只有IE浏览器不支持。
4、Object.getOwnPropertyDescriptors()
提出场景:为了解决Object.assign()
无法正确拷贝get
属性和set
属性的问题。Object.assign
方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法。
ES5 的Object.getOwnPropertyDescriptor()
方法会返回某个对象属性的描述对象(descriptor),而Object.getOwnPropertyDescriptors()
方法,返回指定对象所有自身属性(非继承属性)的描述对象。
const obj = {
foo: 123,
get bar() { return 'abc' }
};
Object.getOwnPropertyDescriptors(obj)
Object.getOwnPropertyDescriptor(obj, foo)
使用Object.assign()
进行拷贝
const source = {
set foo(value) {
console.log(value);
}
};
const target1 = {};
Object.assign(target1, source);
Object.getOwnPropertyDescriptor(target1, 'foo')
上述将 source
对象拷贝给target1
对象,获取属性值时得到了undefined
,是因为Object.assign()
方法总是拷贝一个属性的值,而不会拷贝它背后的赋值方法或取值方法
此时可以使用Object.defineProperties
和 Object.getOwnPropertyDescriptors
配合使用实现拷贝。
const source = {
set foo(value) {
console.log(value);
},
arr: [1, 2, 3]
};
const target2 = {};
Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source));
Object.getOwnPropertyDescriptor(target2, 'foo')
source.arr.push(4)
console.log(source)
console.log(target2)
注意:实现的是浅拷贝
具体应用方式可参考 https://es6.ruanyifeng.com/#Object-getOwnPropertyDescriptors
浏览器支持情况
目前只有IE不支持
三、es9 新特性
1、Promise.prototype.finally()
finally()
方法返回一个Promise
, 在Promise
执行结束时,无论结果是fulfilled
或者是rejected
,都会执行finally
指定的回调函数。
let p = new Promise((resolve, reject) => {
resolve()
})
p.then(() =>
console.log(1) // 1
).catch((err) => {
console.log(err)
}).finally(() => {
console.log('finally') // 'finally'
})
浏览器支持情况
目前只有IE 不支持。
2、对象扩展运算符...
与数组类似,引入了对象扩展运算符,可以将一个对象的属性拷贝到另一个对象上,实现的是浅拷贝。
let form = {
age: 11,
name: 'John'
}
let params = {
...form,
profession: 'student'
}
3、for await ... of
for ... of
循环用来遍历同步的迭代器接口,而for await ... of
是异步迭代器,会等待前一个成员的状态改变后才会遍历到下一个成员,相当于async
函数内部的await
。
function getTime (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(time)
}, time)
})
}
async function test () {
let arr = [getTime(2000), getTime(100), getTime(3000)]
for await (let item of arr) {
console.log(Date.now(), item)
}
}
test()
// 1598251267580 2000
// 1598251267580 100
// 1598251268583 3000
浏览器支持情况
Edge 不支持。
四、es10 新特性
1、Array.prototype.flat()
Array.prototype.flat()
用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。
flat()
方法默认会“拉平”一层,可以传入整数作为参数,表示想要拉平的层数,默认是1。
[1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]]
[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5]
当对嵌套多层的数组想要转成一维数组,可以传入关键字Infinity
。
[1, [2, [3]]].flat(Infinity) // [1, 2, 3]
如果原数组有空位,flat()
会跳过空位。
[1, , 2, , 3, 4].flat() // [1, 2, 3, 4]
浏览器支持情况
目前只有IE 和 Edge不支持。
2、Array.prototype.flatMap()
flatMap()
方法对原数组的每个成员执行一个函数(相当于执行Array.prototype.map()),然后对返回值组成的数组执行flat()
方法。该方法返回一个新数组,不改变原数组。
let arr = [1, 2, 3]
console.log(arr.map(item => [item * 2]).flat()) // [2, 4, 6]
console.log(arr.flatMap(item => [item * 2])) // [2, 4, 6]
实际上flatMap
是综合了map
和flat
的操作,所以它也只能打平一层。
[1, 2, 3, 4].flatMap(x => [x * 2]) // [2, 4, 6, 8]
[1, 2, 3, 4].flatMap(x => [[x * 2]]) // [[2], [4], [6], [8]]
浏览器支持情况
目前只有IE 和 Edge不支持。
3、Object.fromEntries()
Object.fromEntries()
方法是Object.entries()
的逆操作,用于将一个键值对数组转为对象。
const entries = new Map([
['age', 10],
['name', 'Amy']
])
Object.fromEntries(entries) // { age: 10, name: 'Amy' }
浏览器支持情况
目前只有IE、Edge、Opera for Android、Samsung Internet不支持。
4、String.trimStart()/String.timeEnd()
这两个方法的行为与trim()
一致,trimStart()
消除字符串头部的空格,trimEnd()
消除字符串尾部的空格,且不会改变原始字符串。
const str = ' lalallal '
str.trim() // 'lalallal'
str.trimStart() // 'lalallal '
str.trimEnd() // ' lalallal'
浏览器支持情况
目前只有IE不支持。
5、try...catch
catch
中的参数变成一个可选项。
try {
console.log(1)
} catch {
console.log(2)
}
浏览器支持情况
6、Function.prototype.toString()
修改后的toString()
方法,会返回和原函数一模一样的原始代码。
function /* foo comment */ foo () {}
foo.toString() // "function /* foo comment */ foo () {}"
浏览器支持情况
7、Symbol.prototype.description
我们在创建Symbol
时,可以添加一个描述。
读取时要显示转为字符串通过toString()
方法才可以读取。
const sym = Symbol('foo');
String(sym) // "Symbol(foo)"
sym.toString() // "Symbol(foo)"
而description
属性,可以直接方便的返回Symbol
的描述。
const sym = Symbol('foo');
sym.description // "foo"
浏览器支持情况
只有Edge、IE 不支持。
五、es11 新特性
一、ES2020
1、可选链操作符?.
let title = data && data.result && data.result.title
// 之后
let title = data?.result?.title
如果data 或者 data.result是null/undefined
,表达式将会短路计算直接返回undefined。
浏览器支持情况
目前只有Chrome 79 及 Opera 65及以上版本支持。
2、空位合并操作符 ??
// 之前
'' || 'default value' // 'default value'
0 || 'default value' // 'default value'
// 之后
'' ?? 'default value' // ''
??
左侧只有是undefined
和null
时,才返回右侧默认值,否则都为左侧的值。
let c = a ?? b
// 等价于
let c = a !== undefined && a !== null ? a : b
浏览器支持情况
目前只有Chrome 80 及 Firefox 72及以上版本支持。
3、BigInt 任意精度整数
js 只能安全的标识-(2^53-1)至 2^53-1
范围的值,超出这个范围的整数计算会丢失精度。
var num = Number.MAX_SAFE_INTEGER // 9007199254740991
var num1 = num + 1 // 9007199254740992
var num2 = num + 2 // 9007199254740992
9007199254740992 === 9007199254740993 // true
于是产生了BigInt
,它是第七个原始类型,可以进行大数整数运算,使用时需要再数字后面加上n
,或者使用BigInt()
方法进行转化。
var a = 111
var big = BigInt(a)
big === 111n //true
typeof big === 'bigint' // true
typeof 111n // bigint
1222223456789098765n +2n // 1222223456789098767n
浏览器支持情况
目前只有Chrome 67 及 Firefox 68及以上版本支持。
4、import() 动态加载
返回一个Promise 对象,且当加载模块成功以后,这个模块会作为一个对象,当作then
方法的参数。
const modulePage = 'page.js';
import(modulePage)
.then((module) => {
module.default();
});
这种方式也支持 await
关键字。
(async () => {
const helpersModule = 'helpers.js';
const module = await import(helpersModule)
const total = module.sum(2, 2);
})();
浏览器支持情况
目前除了IE和edge不支持,Chrome 是63及以上版本支持。
5、globalThis
用于获取全局this
。
在浏览器中是window
,在 web workers中是self
,在node中是global
。
// 之前
const getGlobal = function(){
if(typeof self !== 'undefined') return self
if(typeof window !== 'undefined') return window
if(typeof global !== 'undefined') return global
throw new Error('unable to locate global object')
}
globalThis
目的就是提供一标准化访问全局对象,而不需要考虑不同的环境问题。
// worker.js
globalThis === self
// node.js
globalThis === global
// browser.js
globalThis === window
浏览器支持情况
目前除了IE、Opera和edge不支持,Chrome 是71及以上版本支持。
6、Promise.allSettled
Promise.all
具有并发执行异步任务的能力,但是最大问题是如果参数中有一个为reject,则整个Promise.all
会立即终止,并返回一个reject的新的Promise对象。
const promises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.reject('error')
];
Promise.all(promises)
.then(responses => console.log(responses))
通常我们可能会用 Promsie.all
来并发请求三个接口,如果一个接口reject了,则会导致三个接口数据全都无法展示,而Promise.allSettled
的出现就可以解决这个痛点。
Promise.allSettled
接受一个Promise 的数组,并返回一个新的数组,与Promise.all
不同的是,它不会进行短路,而是可以拿到每一个Promise的状态。
Promise.allSettled([
Promise.reject({ code: 500, msg: '服务异常' }),
Promise.resolve({ code: 200, list: [] }),
Promise.resolve({ code: 200, list: [] })
]).then(res => {
console.log(res)
/*
0: {status: "rejected", reason: {…}}
1: {status: "fulfilled", value: {…}}
2: {status: "fulfilled", value: {…}}
*/
// 过滤掉 rejected 状态,尽可能多的保证页面区域数据渲染
res.filter(el => {
return el.status !== 'rejected'
})
})
浏览器支持情况
目前除了IE、Opera和edge不支持,Chrome 是76及以上版本支持。
7、String.prototype.matchAll
String.prototype
的match()
方法仅返回完整的匹配结果,而不会返回正则表达式组的信息。
matchAll
方法返回比match更多的信息,会包含全部的正则模式捕获结果,而无需用修饰符/g
。
// match方法
const text = "From 2019.01.29 to 2019.01.30";
const regexp = /(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/gu;
const results = text.match(regexp);
console.log(results);
// [ '2019.01.29', '2019.01.30' ]
// matchAll 方法
const text = "From 2019.01.29 to 2019.01.30";
const regexp = /(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/gu;
const results = Array.from(text.matchAll(regexp));
console.log(results);
// [
// [
// '2019.01.29',
// '2019',
// '01',
// '29',
// index: 5,
// input: 'From 2019.01.29 to 2019.01.30',
// groups: [Object: null prototype] { year: '2019', month: '01', day: '29' }
// ],
// [
// '2019.01.30',
// '2019',
// '01',
// '30',
// index: 19,
// input: 'From 2019.01.29 to 2019.01.30',
// groups: [Object: null prototype] { year: '2019', month: '01', day: '30' }
// ]
// ]
浏览器支持情况
IE、Safari、Edge不支持。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。