盘点平常使用频繁,对开发者有重大意义的变更,无感的基本就没写,感兴趣的可以参考这篇 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"