盘点平常使用频繁,对开发者有重大意义的变更,无感的基本就没写,感兴趣的可以参考这篇 ES3到ES11都增加了什么
ES6
- 1、let 和 const
新的申明变量的方式和变量的作用域
区别1:var声明的变量会挂载在window上,而let和const声明的变量不会
区别2:var声明的变量存在变量提升,而let和const不存在变量提升
区别3:let和const声明形成快作用域
区别4:同一作用域下的let和const不能声明同名变量,而var可以
- 2、字符串方法和模板字符串
// 模板字符串
const name = 'mySkey'
const str = `my name is ${name}`
// startsWith 判定字符串是否以另一个字符串开头,返回布尔值
console.log('hello wolrd'.startsWith('hello')) // true
// endsWith 判定字符串是否以另一个字符串结尾,返回布尔值
console.log('hello wolrd'.endsWith('hello')) // false
// includes 判定字符串是否以包含另一个字符串,返回布尔值
console.log('hello wolrd'.includes('hello')) // true
// repeat 将字符串复制多次,参数就是被复制的次数
console.log('hello wolrd'.repeat(2)) // hello wolrdhello wolrd
- 3、解构赋值
数组解构赋值是根据数组的下标来一一对应的,对象的赋值是根据对象的key来解构
//数组
let [a, b, c] =[1, 2, 3]
//对象
let{ a, b, c} = { a: 1, b: 2, c: 3}
- 4、数组方法
两个静态方法 of 和 from
// Array.of 解决了Array在接收参数时的BUG
console.log(Array.of(3)) // [3]
// Array.from 将类数组对象(之前称为集合的东西,有数组的特点,没数组的方法)转为数组,ES6之前,我们可以通过[].slice.call(arr)方式转为数组
console.log(Array.from(new Set([1, 2, 3])))
三个普通方法 find 、 findIndex 、copyWithin
const testArr = [
{ name: 'ming', age: 23 },
{ name: 'dong', age: 23 }
]
// find 查询数组中的内容,首个满足条件的内容
console.log(testArr.find(item => item.age === 23)) // {name: "ming", age: 23}
// findIndex 与find一致,但是只返回下标
console.log(testArr.findIndex(item => item.age === 23)) // 0
// copyWithin 复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度
const array1 = ['a', 'b', 'c', 'd', 'e'];
console.log(array1.copyWithin(0, 3, 4)); // expected output: Array ["d", "b", "c", "d", "e"]
- 5、对象两个方法 is 和 assign
// Object.is 用于比较两者是否全等
console.log(NaN === NaN) //false
console.log(Object.is(NaN,NaN) //true
// Object.assign 用于对象属性的复制(浅复制)
var obj = {}
var obj1 = {sex :'女'}
Object.assign(obj, obj1)
- 6、箭头函数
javascript中容易混淆作用域,一般都要利用词法作用域来传递 this,es6就解决了这个问题
// 一个参数时,可以不用()包含形参; 一句执行语句时,可以不用{}包含代码块; 一行代码时是有返回值的。
let fn = num=>console.log(num)
// 两个参数
let fn = (a,b)=>a+b
// 箭头函数中的this
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 }); // id: 42
以下几点需要注意:
(1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
(2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
(3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
双冒号的使用,箭头函数可以绑定this对象,大大减少了显式绑定this对象的写法(call、apply、bind)。但是,箭头函数并不适用于所有场合,所以现在有一个提案,提出了“函数绑定”(function bind)运算符,用来取代call、apply、bind调用。
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
- 7、reset符的使用 …
// 函数参数中使用
let fn = (a,...arguments)=>console.log(...arguments)
fn(1,2,3,4) // 2,3,4
// 对象中使用
let person = {
name: 'mySkey',
age: 23
}
let me = {...person}
console.log(me) // { name: 'mySkey , age: 23}
let showInfo = ({name, age})=>{
console.log(name +'`s age is '+ age)
}
showInfo({ ...person })
// 数组中使用
let arr = [1,2,3,4]
let arr1 = [0,...arr,5]
console.log(arr1) // [0,1,2,3,4,5]
- 8、Proxy
ES6中增加了一个Proxy代理类,他可以对指定的对象的访问操作和修改操作进行拦截,vue2.0是通过Object.defineProperty来拦截,要是开启深层拦截将很耗费性能,所以对于对象,数组的更改,无法数据动态响应;在vue3.0中将使用Proxy来进行拦截,将解决这一问题。
var proxy = new Proxy(原对象,{
get:function(原对象,属性名){},
set:function(原对象,属性名,属性值){},
})
- 9、Set 和 WeakSet
Set 它类似于数组,但是成员的值都是唯一的,没有重复的值
使用场景并不多,但是用来做数组去重很方便
let removeSame = arr=>[...new Set(arr)]
既然可以做数组去重,那就可以做字符串去重
let removeSameStr = str=>[...new Set(str)].join('')
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。1、WeakSet 的成员只能是对象 2、WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中
const a = [[1, 2], [3, 4]];
const ws = new WeakSet(a);
// WeakSet {[1, 2], [3, 4]}
- 10、Map 和 WeakMap
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
const map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
map.has('title') // true
map.get('title') // "Author"
WeakMap 结构与Map结构类似,也是用于生成键值对的集合。WeakMap与Map的区别有两点:1、WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名 2、WeakMap的键名所指向的对象,不计入垃圾回收机制
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}
- 11、Symbol
ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第六类型,前五是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)。
let mySymbol = Symbol();
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
// 第二种写法
let a = {
[mySymbol]: 'Hello!'
};
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"
- 12、Promise
Promise是es6用来处理异步的对象,分为三种状态:pending、fulfilled、rejected。对象拥有resolve和reject两个方法,分别在里面处理正确情况和异常情况。
let getData = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
let data = { code: 404, msg: 'I am sleeping' }
try{
// throw('我非要出个错,来看一下catch有没有用') // throw 强制产生一个错误,Promis的catch就能捕获到错误
resolve(data)
}catch(err){
reject(err)
}
}, 3000)
})
}
// 调用函数,在函数返回时使用数据
getData().then(res=>{
console.log('后台说:' + res.msg)
}).catch(err=>{
console.log('发生的错误是:' + err)
})
- 13、Generator
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。在node的中间件中使用广泛,中间件授权之类,vue的路由鉴权也有使用
let getData = ()=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{
let data = { code: 404, msg: 'I am sleeping' }
resolve(data)
}, 3000)
})
}
// 调用函数,在函数返回时使用数据
(function* () {
let data = yield getData();
return data;
})().next().value.then(res=>{
console.log(res)
})
- 14、Iterator 和 for…of 循环
avaScript 原有的表示“集合”的数据结构,主要是数组(Array)和对象(Object),ES6 又添加了Map和Set。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map,Map的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。遍历器可以直接遍历迭代器的结果,而不需要每次next
function* foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5
- 15、Class
class的本质是function,它可以看做是一个语法糖,让对象原型的写法更加清晰,更像面向对象编程的写法
class Person {
constructor(props, name, age){
super(props);
this.name = name;
this.age = age;
}
sing(){
console.log('sing');
}
cry(){
console.log('cry');
}
}
Person.prototype = {
talk: function(){
console.log('talk')
}
}
let mySkey = new Person('mySkey', 23)
mySkey.talk()
16、模块化
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
ES7
- 1、Array.prototype.includes()
includes() 函数用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回false。
arr.includes(x)
// 相当于
arr.indexOf(x) >= 0
- 2、指数操作符
在ES7中引入了指数运算符**,** 有与Math.pow(…)等效的计算结果。
console.log(2**10);// 输出1024
ES8
- 1、async/await
async,await异步解决方案:优化回调地狱,ES6中的promise解决了回调地狱,但是要连续打点调用then,就会显得不清楚,所以引入了async函数,awiat表达式
async function process(array) {
for await (let i of array) {
doSomething(i);
}
}
- 2、Object.values()
Object.values()就是将对象的值合并到一个数组中, Object.keys() 是将对象的key合并到一个数组中
const obj = {a: 1, b: 2, c: 3};
console.log(Object.values(obj)) // [1, 2, 3]
- 3、Object.entries()
Object.entries()函数返回一个给定对象自身可枚举属性的键值对的数组。
for(let [key,value] of Object.entries(obj1)){
console.log(`key: ${key} value:${value}`)
}
//key:a value:1
//key:b value:2
//key:c value:3
4、string padStart() 和 padEnd
允许将空字符串或其他字符串添加到原始字符串的开头或结尾。
console.log('8'.padStart(2, '0'))
5、函数参数列表结尾允许逗号
主要作用是方便使用git进行多人协作开发时修改同一个函数减少不必要的行变更。
function add(
a,
b,
){
return a + b
}
ES9
- 1、异步迭代
在async/await的某些时刻,你可能尝试在同步循环中调用异步函数。例如:
// 以下两种方式就循环本身依旧保持同步
async function process(array) {
for (let i of array) {
await doSomething(i);
}
}
async function process(array) {
array.forEach(async i => {
await doSomething(i);
});
}
// 想要同步循环,需要以下写法
async function process(array) {
for await (let i of array) {
doSomething(i);
}
}
- 2、Promise.finally()
一个Promise调用链要么成功到达最后一个.then(),要么失败触发.catch()。在某些情况下,你想要在无论Promise运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。
function doSomething() {
doSomething1()
.then(doSomething2)
.then(doSomething3)
.catch(err => {
console.log(err);
})
.finally(() => {
// finish here! // 清除loading之类的
});
}
- 3、Rest/Spread 属性
ES2015引入了Rest参数和扩展运算符。三个点(…)仅用于数组。Rest参数语法允许我们将一个不定数量的参数表示为一个数组。
function restParam({ a, ...x }) {
// a = 1
// x = { b: 2, c: 3 }
}
restParam({
a: 1,
b: 2,
c: 3
});
const myObject = {
a: 1,
b: 2,
c: 3
};
const { a, ...x } = myObject;
JavaScript正则表达式可以返回一个匹配的对象——一个包含匹配字符串的类数组,例如:以YYYY-MM-DD的格式解析日期:
const
reDate = /([0-9]{4})-([0-9]{2})-([0-9]{2})/,
match = reDate.exec('2018-04-30'),
year = match[1], // 2018
month = match[2], // 04
day = match[3]; // 30
ES2018允许命名捕获组使用符号?<name>
,在打开捕获括号(后立即命名,示例如下:
const
reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
match = reDate.exec('2018-04-30'),
year = match.groups.year, // 2018
month = match.groups.month, // 04
day = match.groups.day; // 30
命名捕获也可以使用在replace()方法中。例如将日期转换为美国的 MM-DD-YYYY 格式:
const
reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,
d = '2018-04-30',
usDate = d.replace(reDate, '$<month>-$<day>-$<year>');
ES10
- 1、Array的flat()方法和flatMap()方法
flat()和flatMap()本质上就是是归纳(reduce) 与 合并(concat)的操作。
// flat()方法最基本的作用就是数组降维
arr3.flat(Infinity); /使用 Infinity 作为深度,展开任意深度的嵌套数组
// [1, 2, 3, 4, 5, 6]
// 可以利用flat()方法的特性来去除数组的空项
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]
flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。 这里我们拿map方法与flatMap方法做一个比较。
var arr1 = [1, 2, 3, 4];
arr1.map(x => [x * 2]);
// [[2], [4], [6], [8]]
arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]
// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]
- 2、String的trimStart()方法和trimEnd()方法
分别是去除首位空白符的
- 3、Object.fromEntries() 和 bject.entries()
Object.entries()方法的作用是返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for…in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。而Object.fromEntries() 则是 Object.entries() 的反转。
const map = new Map([ ['foo', 'bar'], ['baz', 42] ]);
const obj = Object.fromEntries(map);
console.log(obj); // { foo: "bar", baz: 42 }
const arr = [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ];
const obj = Object.fromEntries(arr);
console.log(obj); // { 0: "a", 1: "b", 2: "c" }
- 4、Function.prototype.toString()
现在返回精确字符,包括空格和注释
- 5、catch参数非必须
try {} catch {}
- 6、Symbol.prototype.description
通过工厂函数Symbol()创建符号时,您可以选择通过参数提供字符串作为描述:
const sym = Symbol('The description');
assert.equal(sym.description, 'The description');
ES11
使用ES11需要安装的babel插件
plugins: [
"@babel/plugin-proposal-nullish-coalescing-operator",
"@babel/plugin-proposal-optional-chaining",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods",
"@babel/plugin-syntax-bigint"
]
- 1、Nullish coalescing Operator(空值处理)
如果表达式在 ??
的左侧 运算符求值为undefined或null,返回其右侧。
let user = {
u1: 0,
u2: false,
u3: null,
u4: undefined
u5: '',
}
let u1 = user.u1 || '用户1' // 用户1
let u2 = user.u2 || '用户2' // 用户2
let u3 = user.u3 || '用户3' // 用户3
let u4 = user.u4 || '用户4' // 用户4
let u5 = user.u5 || '用户5' // 用户5
// es11语法
let u1 = user.u1 ?? '用户1' // 0
let u2 = user.u2 ?? '用户2' // false
let u3 = user.u3 ?? '用户3' // 用户3
let u4 = user.u4 ?? '用户4' // 用户4
let u5 = user.u5 ?? '用户5' // ''
- 2、Optional chaining(可选链)
?.
用户检测不确定的中间节点,如果不存在中间节点则返回undefined。避免了程序报错直接导致整个应用挂掉,可以用lodash的 get
来处理这种链路不确定性
let person = {}
let age = person.info.age // TypeError: Cannot read property 'name' of undefined 因为person的info属性为undefined,那么直接就报错了
let age = person.info?.age // undefined
使用lodash
import _ from 'lodash'
let age = _.get(person, 'person.info.age', '')
- 3、romise.allSettled
使用 Promise.all 来并发两个接口,如果其中任意一个异常,则两个区域都无法正常渲染。Promise.allSettled 则可以避免这个问题
Promise.all([
new Promise.reject('a1'),
new Promise.resolve('a2')
]).then((ret) => {
// 不会执行
console.log(ret)
}).catch((error) => {
// 因为有一个promise返回为reject。所以程序只会走到这里
// 输出:a1
console.log(error)
})
// 使用es11的Promise.allSettled
Promise.allSettled([
new Promise.reject('a1'),
new Promise.resolve('a2')
]).then((ret) => {
// 输出
// 0: {status: "fulfilled", value: "a1"},
// 1: {status: "rejected", value: "a2"}
console.log(ret)
// 这样可以过滤掉rejected,避免整段程序运行错乱
handleFun(ret.filter(el => el.status !== 'rejected'))
})
- 4、Dynamic import
动态引入,之前的import是静态引入
const util = './util.js'
import(util).then((module) => {
module.fun1();
module.fun2();
});
(async () => {
const util = './util.js';
const module = await import(util)
const fun1 = module.fun1(1);
const fun2 = module.fun1(2);
})();
- 5、BigInt
BigInt是第7个基本类型,它是一个任意精度的整数。变量可以代表2⁵³不仅是在9007199254740992处的最大值。
// 创建方法一:在整数后面加 n
let bigInt = 9999999999999999n;
// 创建方法二:BigInt函数
let bigInt2 = BigInt(9999999999999999);
let bigInt3 = BigInt('9999999999999999');
// bigIng类型可以进行更大的值计算
let sum = bigInt + bigInt2 // 19999999999999999n
sum.toString() // 19999999999999999 使用toString可以去掉后面的n
// bigint是一种新的原始类型
typeof 9999999999999999n; // -> 'bigint
- 6、String.protype.matchAll
原有的 match() 方法仅返回完整的匹配结果,却不会返回特定正则表达式组。而 matchAll()返回的迭代器不仅包括精确的匹配结果,还有全部的正则模式捕获结果
var str = 'From 2019.01.29 to 2019.01.30';
var allMatchs = str.matchAll(/(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/g);
for (const match of allMatchs) {
console.log(match);
}
// [
// [
// '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' }
// ]
// ]
- 7、globalThis
全局this。在浏览器中它是 window, 在 worker 中它是 self, 在 Node.js 中它是global。有了globalThis后不管在哪个平台都可以用它来表示顶级的this
- 8、import.meta
Domenic Denicola 提出的 import.meta 提议为当前运行的模块添加了一个特定 host 元数据对象。
console.log(import.meta.url)
// file:///Users/pawelgrzybek/main.js
- 9、export * as ns from “mod”
这是对 ES 规范的有力补充,它允许开发者以新名称导出另一模块的命名空间外部对象。用于集成多个模块到一个文件
export * as ns from "mod"