2021年6月22日星期二

ES2015+ 常用新特性一口气看个够

ES2015 也叫 ES6,区别只是一个是以发布的年份来命名,一个是以版本号来命名

从那以后组织每年都会发布一个新版本,根据这个规则,ES2016 === ES7... ES2020 === ES11

但通常我习惯将 ES2015 及其后续版本统称为 ES2015+

变量声明

ES2015 增加了两个声明变量标识符的关键字,letconst,两者都支持块级作用域,并且在声明之前不能访问

凡是不需要重新赋值的变量标识符都可以使用 const 关键字来声明

其余需要重新赋值的变量就使用 let 关键字来声明,像循环计数器之类的

{ const arr = ['a', 'b', 'c', 'd', 'e'] const length = arr.length for (let i = 0; i < length; i++) { // }}// 块之外无法访问

对象字面量

对象字面量的简写形式以及计算属性

const foo = 1const obj = { foo, // 属性简写,等同于 foo: foo, bar() { // 方法简写 }, // 计算属性 ['na' + 'me']: 'by.Genesis', __proto__: 原型}

这些特性都可以简化原本的代码

__proto__ 用来 get/set 原型,不过并不推荐使用,应该使用 Object.getPrototypeOf(o)Object.setPrototypeOf(o, proto)

箭头函数

箭头(=>)就是一种函数简写方式,同时提供一些有用的特性

// 当函数有且仅有一个参数的时候可以省略参数的圆括号;[1, 2, 3].forEach(item => { console.log(item) })// 当函数体内只有一条语句的时候可以省略函数体的花括号,同时隐式返回该条语句const sum = (x, y) => x + y// 如果隐式返回的是一个对象字面量,为了消除歧义,可以使用一对圆括号包裹对象字面量const pos = (x, y) => ({ x: x + 1, y: y * 2 })// 词法 thisconst obj = { name: 'by.Genesis', showName() { setTimeout(() => {  console.log(this.name) // obj.showName() this === obj }, 300) }}// 立即执行箭头函数表达式;(() => { alert(101)})()

class

类(class)就是传统的构造函数基于原型继承的语法糖

class Person { constructor(name, age) { this.name = name this.age = age } update() { // 方法 }}// 继承class Student extends Person { constructor(name, age, grade) { super(name, age) this.grade = grade } update() { // 调用父类的方法 super.update() } get foo() { // getter } set foo() { // setter } static baz() { // 静态方法通过 Student.baz() 调用 }}const s1 = new Student('by.Genesis', 20, 2)

同函数一样,类也可以作为表达式赋值给一个变量,或者作为参数传给函数,甚至从函数中返回

const Person = class { //}

无论是用类声明还是表达式,都需要先定义,然后再使用,不会提升,不会提升

Symbol

符号(Symbol)是一种新的原始类型,其没有字面量形式

符号可以分为3类,普通符号,全局符号和众所周知的符号(well-known Symbol)

// 创建 Symbol,不需要 new// 传入的参数作为该 Symbol 的描述符const name = Symbol('name')// 将 Symbol 用作对象的 key// 只能使用可计算属性名的方式const o = { [name]: 'by.Genesis'}// 通过 typeof 操作符判断值类型typeof Symbol('name') === 'symbol'// 符号值是唯一的,就算在创建时传入了相同的参数,得到的符号也不是同一个Symbol('name') !== Symbol('name')// 不过在全局符号注册表中同一个 key 返回的是同一个符号Symbol.for('name') === Symbol.for('name')// 获取符号描述符Symbol('name').description === 'name'

对象的符号属性无法通过传统的方法遍历出来,需要的时候可以使用 Object.getOwnPropertySymbols() 方法获取参数对象中所有符号属性组成的数组

Object.getOwnPropertySymbols(o) // [Symbol(name)]

除此之外,还有一些 众所周知的符号(well-known Symbol),这类符号的作用是暴露一些 JavaScript 内部操作

// 当数组作为 concat 参数时,默认会被展开const arr = [4, 5, 6];[1, 2, 3].concat(arr) // [1, 2, 3, 4, 5, 6]// 可以修改此行为让数组参数不展开arr[Symbol.isConcatSpreadable] = false;[1, 2, 3].concat(arr) // [1, 2, 3, [4, 5, 6]]// 也可以让类数组对象展开;[1, 2, 3].concat({ [Symbol.isConcatSpreadable]: true, length: 3, 0: 4, 1: 5, 2: 6}) // [1, 2, 3, 4, 5, 6]

Promise

Promise 主要用来表示一个未来值

// 创建一个 promiseconst p = new Promise((resolve, reject) => { setTimeout(resolve, 3000, 'by.Genesis')})// promise resolve 时执行p.then(res => { console.log(res) // 'by.Genesis'})// 总是会执行,无论 resolve 还是 rejectp.finally(() => console.log('finally'))// 立即创建一个 fulfilled 的 promiseconst p2 = Promise.resolve('101真狗')// 立即创建一个 rejected 的 promiseconst p3 = Promise.reject(404)// promise reject 时执行p3.catch(err => { console.log(err) // 404})// 等待一组 promise 全部 resolve// 一旦有一个为 rejected 则立即 rejectPromise.all([p2, p3])// 获取一组 promise 中最快的那一个,无论 resolve 还是 rejectPromise.race([p2, p3])// 等待一组 promise 全部 settled,无论 resolve 还是 rejectPromise.allSettled([p2, p3])// 获取一组 promise 中最快 resolve 的那一个// 只有当全部都为 rejected 的时候 rejectPromise.any([p2, p3])

迭代器和生成器

当一个对象拥有一个 next 方法,并且调用该方法时可以得到一个包含 donevalue 两个属性的结果对象,那么这就是一个迭代器(Iterator)

iterator = { next() { return {  done: false,  value: 10 } }}

其中 done 为 Boolean 类型,表示该迭代器是否已经迭代完毕

生成器(Generator)是一种特殊函数,声明的时候在 function 关键字和函数名中间多了个星号(*)

生成器内通过 yield 关键字返回值

function *g() { yield 1 yield 2 yield 3}// 调用生成器可以得到一个迭代器iterator = g()// 调用迭代器的 next 方法执行生成器内部代码并得到结果对象iterator.next() // { value: 1, done: false }

当一个对象具有特殊的符号 [Symbol.iterator] 方法,并且该方法返回一个迭代器的时候,那么这个对象就是一个可迭代对象(Iterable)

iterable = { *[Symbol.iterator]() { // 这里同时使用了对象方法简写,计算属性以及生成器 yield 1 yield 2 yield 3 }}

String,Array,Map,Set,NodeList 等等都是可迭代对象,迭代器自身也是可迭代对象,迭代器的 [Symbol.iterator] 方法返回自身

生成器可以通过 yield* 委托给其它可迭代对象

iterable = { *[Symbol.iterator]() { yield 1 yield* [2, 3] }}

可迭代对象可以使用 for of 语法遍历

for (let v of iterable) { console.log(v) // 1 2 3}

异步函数

异步函数(Async function)在函数前面添加一个 async 关键字,其内部可以使用 await 关键字

await 表达式可以将其后面的 Promise resolve 的值提取出来

async function fn() { const x = await Promise.resolve(101) return x}// 执行异步函数也返回一个 Promisefn().then(res => console.log(res)) // 101

异步函数就是生成器和 Promise 语法糖

异步迭代

当一个迭代器的 next 方法返回一个 Promise,并且该 Promise resolve 后可以得到一个包含 donevalue 两个属性的结果对象,那么这个迭代器就是一个异步迭代器(Async Iterator)

asyncIterator = { next() { return Promise.resolve({  done: false,  value: 10 }) }}

将异步函数和生成器结合到一起,就是异步生成器(Async Generator),其内部可以同时使用 awaityield 关键字

async function *g() { yield 1 const a = await new Promise(resolve => { setTimeout(resolve, 3000, 2) }) yield a yield Promise.resolve(a + 1)}// 执行异步生成器返回一个异步迭代器asyncIterator = g()

当一个对象具有特殊的符号 [Symbol.asyncIterator] 方法,并且该方法返回一个异步迭代器的时候,那么这个对象就是一个异步可迭代对象(Async Iterable)

asyncIterable = { async *[Symbol.asyncIterator]() { yield 1 const a = await new Promise(resolve => {  setTimeout(resolve, 3000, 2) }) yield a yield Promise.resolve(a + 1) }}

异步可迭代对象使用 for await of 语法遍历

;(async () => { for await (let v of asyncIterable) { console.log(v) // 1 2 3 }})()

await 应该放到异步函数中

Map & Set

Map 是包含键值对(key-value)的有序集合,其中 key 可以是 任意类型 (是任意类型,包括引用类型甚至 DOM 元素都可以作为 Map 的 key)

// 创建一个 Mapconst m = new Map([['a', 1], ['b', 2]])// 添加值,如果已存在就是修改m.set('c', 3)// 获取值m.get('b') // 2// 判断值m.has('b') // true// 获取长度m.size // 3// 删除值m.delete('b')m.has('b') // falsem.size // 2// 清空m.clear()m.size // 0

Set 就是一组不重复值的有序集合

// 创建一个 Setconst s = new Set([1, 2])// 添加值s.add(3)s.add(1) // 该值已存在,集合保持不变// 除了没有获取值的方法,剩下的和 Map 一致

Map 和 Set 都可以通过 forEach 方法遍历其中的值

set.forEach(handler => handler())

可以把 Set 看作是 key 和 value 为同一个值的特殊 Map,也可以认为 Set 是只有 key

Map 和 Set 遍历顺序和添加时的顺序是一致的,因此都是有序集合

WeakSet & WeakMap

弱版本只能用来存放引用类型

WeakMap 只对其 key 有类型要求,而 value 可以是任意类型

弱版本不是可迭代对象,不能遍历,也没有 size 属性,也不能用 clear 方法清空集合,只具备最基本的添加,删除等方法

弱版本是弱引用,其优势就是利于垃圾回收

解构

按照一定模式从对象或者可迭代对象中提取值

// 可迭代对象解构// let 声明对变量 a, b, c 都生效let [a, b, c] = [1, 2]a === 1b === 2c === undefined// 交换值;[a, b] = [b, a] // a = 2, b = 1// 数组可以解构任意可迭代对象,包括字符串// 解构时也可以跳过一些不需要的值;[, , c] = '123' // c = '3'// 对象属性解构{ x, y, z: { w } } = { x: 3, y: 4, z: { w: 5 } }a === 3b === 4w === 5

默认值

在声明函数参数或者解构的时候都可以指定一个默认值,当对应的值为 undefined 的时候,就会使用这个默认值

// 函数参数默认值const sum = (x, y = 4) => x + ysum(3) === 7// 迭代器解构的默认值const [a, b = 2] = [1]a === 1b === 2// 对象解构的默认值const { name = 'by.Genesis' } = { age: 18 }name === 'by.Genesis'// 函数参数和对象解构一起使用const fn = ({ height = 18, width = 36 } = {}) => {}fn() // height = 18, width = 36fn({ height: 36 }) // height = 36, width = 36fn({ height: 36, width: 18 }) // height = 36, width = 18

Spread & Rest

可迭代对象均可使用展开(Spread)运算符(...)展开为独立的值,这些值可以作为函数的参数或放到数组中

// 展开可迭代对象作为函数参数Math.max(...[5, 20, 10]) === 20// 展开可迭代对象到一个数组中const arr = [...new Set([1, 2, 2, 3])] // [1, 2, 3]// 展开可迭代对象到一个数组中const newArr = [1, ...[2, 3], ...'45'] // [1, 2, 3, '4', '5']

而普通对象也可以展开其属性,放到另一个对象中,这和 Object.assign 方法作用类似

// 展开对象属性到另一个对象中const o = { a: 1, ...{ b: 2, c: 3 }} // o = { a: 1, b: 2, c: 3 }const o2 = Object.assign({ a: 1 }, { b: 2, c: 3 })

和展开相反,多个值可以使用收集(Rest)运算符(...)打包成一个数组,或者多个对象属性打包成一个对象

// 函数剩余参数打包成一个数组const fn = (x, ...y) => y.lengthfn(2, 5, 7, 11) === 3 // x = 2, y = [5, 7, 11]// 可迭代对象剩余值打包成一个数组const [a, ...b] = new Set([2, 5, 7, 11])a === 2// b = [5, 7, 11]// 对象剩余属性打包成一个对象const { a, ...o } = { a: 1, b: 2, c: 3} // o = { b: 2, c: 3 }

收集运算符只能用于最后一个标识符

模板字符串

模板字符串就是功能更强大的字符串,它支持多行以及插值

在模板字符串中插值使用 ${} 花括号里面可以插入表达式,表达式甚至可以是另一个模板字符串

const str = `<ul> ${lists.map(item => { return `<li>${item.user} is ${item.age} years old.</li>` }).join('')}</ul>`

标签模板

const username = 'by.Genesis'const age = 18const str = tag`${username} is ${age} years old.`// tag 就是一个函数// 第一个参数为字符串按插值分割而成的数组// 后面的参数为插值表达式的值// 可以自行处理字符串逻辑function tag(template, ...substitutions) { console.log(template) // ['', ' is ', ' years old.'] console.log(substitutions) // ['by.Genesis', 18] return substitutions[0] + template[1] + 'handsome'}str === 'by.Genesis is handsome'

代理和反射

代理(Proxy)就是为一个目标对象生成一个代理,当对这个代理对象执行一些操作的时候,就会触发对应的拦截器,在拦截器中可以自行定义操作和返回的值,或者用反射(Reflect)执行元操作,每个代理方法都有对应的反射方法

const obj = {}const proxy = new Proxy(obj, { get(target, key) { // 属性取值 if (key === 'name') {  // 自定义返回值  return 'by.Genesis' } else {  // 用反射还原操作  return Reflect.get(target, key) } }, set() { 属性赋值 }, has() { in 操作符 }, deleteProperty() { 删除属性 }, getPrototypeOf() { 获取原型 }, setPrototypeOf() { 设置原型 }, defineProperty() { Object.defineProperty }, getOwnPropertyDescriptor() { Object.getOwnPropertyDescriptor }, preventExtensions() { Object.preventExtensions }, isExtensible() { Object.isExtensible }, ownKeys() { Object.keys, Object.getOwnPropertyNames, Object.getOwnPropertySymbols, Object.assign }, enumerable() { for in 循环 }, apply() { 函数普通调用 }, construct() { new 方式调用函数 }})proxy.name === 'by.Genesis'obj.name === undefined

以上是这些拦截器以及对应的触发条件

逻辑运算

Nullish coalescing Operator

JavaScript 里面的假值(Falsy)有 null, undefined, 0, '', NaN, false,除假值外都为真值(Truthy)

而空值(Nullish)只有 nullundefined,当该运算符左侧为空值时返回右侧

null ?? 1 // 1undefined ?? 1 // 10 ?? 1 // 00 || 1 // 1

Optional chaining

链式操作时,当中间某个值是 null 或者 undefined 就会报错,而这个操作符可以让链式操作更安......

原文转载:http://www.shaoqun.com/a/822693.html

跨境电商:https://www.ikjzd.com/

西集网:https://www.ikjzd.com/w/1353

feedly:https://www.ikjzd.com/w/754

海维:https://www.ikjzd.com/w/1891


ES2015也叫ES6,区别只是一个是以发布的年份来命名,一个是以版本号来命名从那以后组织每年都会发布一个新版本,根据这个规则,ES2016===ES7...ES2020===ES11但通常我习惯将ES2015及其后续版本统称为ES2015+变量声明ES2015增加了两个声明变量标识符的关键字,let和const,两者都支持块级作用域,并且在声明之前不能访问凡是不需要重新赋值的变量标识符都可以使用
北京的旅游景点有哪些?北京旅游景区推荐:http://www.30bags.com/a/401569.html
江西南昌特产:胡卓人蕲蛇药酒 - :http://www.30bags.com/a/407616.html
Amazon Alexa:https://www.ikjzd.com/w/62
Amazon Business:https://www.ikjzd.com/w/63
最强电竞双性恋者:虐待所有女选手后变性,最终击败韩国夺得奥运冠军!:http://www.30bags.com/a/450916.html
怎么能吃兔子?你愿意吃这么逼真的兔子吗?:http://lady.shaoqun.com/a/383275.html
男人是最能吃的女人,让他听话,逃不出手掌心:http://lady.shaoqun.com/a/383276.html
askme:https://www.ikjzd.com/w/2459
中转贸易:https://www.ikjzd.com/w/1427
Add-on Item:https://www.ikjzd.com/w/61
口述实录:嫌男友没文化 家人棒打鸳鸯阻挠我恋爱:http://lady.shaoqun.com/m/a/253300.html
两根一起进去好紧好涨 两根粗大黑肉来回进出:http://lady.shaoqun.com/a/247432.html

没有评论:

发表评论