欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ES的新特性(ES6、ES7、ES8)

程序员文章站 2024-03-18 11:00:04
...

写在前面:本篇文章是来自公众号前端大全为文章,仅为自己记录,方便学习!

1、Let和Const
let 声明的变量只在所在块级作用域中可用
const 声明的为常量,不可重新赋值
var 、let 、const常见区别如下:ES的新特性(ES6、ES7、ES8)

2、Class(类)

ES6之前,如果我们生成实例对戏,传统方法是写一个构造函数,如下:

function Person(name, age) {
    this.name = name
    this.age = age
}
Person.prototype.information = function () {
    return 'My name is ' + this.name + ', I am ' + this.age
}

有了Class之后

class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }
    information() {
        return 'My name is ' + this.name + ', I am ' + this.age
    }
}

3、箭头函数
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或 new.target。这些函数表达式更适用于那些本来需要匿名函数的地方,并且它们不能用作构造函数。

在ES6以前,我们写函数一般是:

var list = [1, 2, 3, 4, 5, 6, 7]
var newList = list.map(function (item) {
    return item * item
})

但是在ES6有箭头函数之后,我们可以:

const list = [1, 2, 3, 4, 5, 6, 7]
const newList = list.map(item => item * item)

4、函数参数默认值
在ES6之前,如果我们写函数需要定义初始值的时候,需要这么写:

function config (data) {
    var data = data || 'data is empty'
}

这样看起来也没有问题,但是如果参数的布尔值为false时就会出问题,例如我们这样调用config:

config(0)
config('')

那么结果就永远是后面的值

如果我们用函数参数默认值就没有这个问题,写法如下:

const config = (data = 'data is empty') => {}

5、模板字符串
在ES6之前,如果我们要拼接字符串,则需要像这样:

var name = 'kris'
var age = 24
var info = 'My name is ' + this.name + ', I am ' + this.age

但是在ES6之后,我们只需要写成以下形式:(用反引号包含内容、用${}引用数据)

const name = 'kris'
const age = 24
const info = `My name is ${name}, I am ${age}`

6、解构赋值
解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。

比如需要交换两个变量的值,之前需要:

var a = 10
var b = 20
var temp = a
a = b
b = temp

但是在ES6里,我们有:

let a = 10
let b = 20
[a, b] = [b, a]

7、模块化(Module)
在ES6之前,JS并没有模块化的概念,有的也只是社区定制的类似CommonJS和AMD之类的规则。例如基于CommonJS的NodeJS:

// circle.js
// 输出
const { PI } = Math
exports.area = (r) => PI * r ** 2
exports.circumference = (r) => 2 * PI * r

// index.js
// 输入
const circle = require('./circle.js')
console.log(`半径为 4 的圆的面积是 ${circle.area(4)}`)

在ES6之后我们则可以写成以下形式:

/

/ circle.js
// 输出
const { PI } = Math
export const area = (r) => PI * r ** 2
export const circumference = (r) => 2 * PI * r

// index.js
// 输入
import {
    area
} = './circle.js'
console.log(`半径为 4 的圆的面积是: ${area(4)}`)

8、扩展操作符
扩展操作符可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造字面量对象时, 将对象表达式按key-value的方式展开。

比如在ES5的时候,我们要对一个数组的元素进行相加,在不使用reduce或者reduceRight的场合,我们需要:

function sum(x, y, z) {
    return x + y + z;
}
var list = [5, 6, 7]

var total = sum.apply(null, list)
但是如果我们使用扩展操作符,只需要如下:

const sum = (x, y, z) => x + y + z
const list = [5, 6, 7]
const total = sum(...list)

非常的简单,但是要注意的是扩展操作符只能用于可迭代对象

如果是下面的情况,是会报错的:

var obj = {'key1': 'value1'}
var array = [...obj] // TypeError: obj is not iterable

9、对象属性简写
在ES6之前,如果我们要将某个变量赋值为同样名称的对象元素,则需要:

var cat = 'Miaow'
var dog = 'Woof'
var bird = 'Peet peet'
var someObject = {
  cat: cat,
  dog: dog,
  bird: bird
}

但是在ES6里我们就方便很多:

let cat = 'Miaow'
let dog = 'Woof'
let bird = 'Peet peet'
let someObject = {
  cat,
  dog,
  bird
}
console.log(someObject)
//{
//  cat: "Miaow",
//  dog: "Woof",
//  bird: "Peet peet"
//}

10、Promise

Promise 是ES6提供的一种异步解决方案,比回调函数更加清晰明了。

Promise 有三种状态,分别是:1.等待中(pending)2.完成了 (resolved)3.拒绝了(rejected)

这个承诺一旦从等待状态变成为其他状态就永远不能更改状态了,也就是说一旦状态变为 resolved 后,就不能再次改变

new Promise((resolve, reject) => {
  resolve('success')
  // 无效
  reject('reject')
})

当我们在构造 Promise 的时候,构造函数内部的代码是立即执行的

new Promise((resolve, reject) => {
  console.log('new Promise')
  resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh

Promise 实现了链式调用,也就是说每次调用 then 之后返回的都是一个 Promise,并且是一个全新的 Promise,原因也是因为状态不可变。如果你在 then 中 使用了 return,那么 return 的值会被 Promise.resolve() 包装

Promise.resolve(1)
  .then(res => {
    console.log(res) // => 1
    return 2 // 包装成 Promise.resolve(2)
  })
  .then(res => {
    console.log(res) // => 2
  })

当然了,Promise 也很好地解决了回调地狱的问题,例如:

ajax(url, () => {
    // 处理逻辑
    ajax(url1, () => {
        // 处理逻辑
        ajax(url2, () => {
            // 处理逻辑
        })
    })
})

可以改写成

ajax(url)
  .then(res => {
      console.log(res)
      return ajax(url1)
  }).then(res => {
      console.log(res)
      return ajax(url2)
  }).then(res => console.log(res))

11、for …of
for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。

例子如下:

const array1 = ['a', 'b', 'c'];

for (const element of array1) {
      console.log(element)
}

// "a"
// "b"
// "c"

12、Symbol

symbol 是一种基本数据类型,Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:“new Symbol()”。

每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。

例子如下:

const symbol1 = Symbol();
const symbol2 = Symbol(42);
const symbol3 = Symbol('foo');

console.log(typeof symbol1); // "symbol"
console.log(symbol3.toString()); // "Symbol(foo)"
console.log(Symbol('foo') === Symbol('foo')); // false

13、迭代器(Iterator)/ 生成器(Generator)

迭代器(Iterator)是一种迭代的机制,为各种不同的数据结构提供统一的访问机制。任何数据结构只要内部有 Iterator 接口,就可以完成依次迭代操作。

一旦创建,迭代器对象可以通过重复调用next()显式地迭代,从而获取该对象每一级的值,直到迭代完,返回{ value: undefined, done: true }

虽然自定义的迭代器是一个有用的工具,但由于需要显式地维护其内部状态,因此需要谨慎地创建。生成器函数提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。生成器函数使用 function*[2]语法编写。最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator的迭代器。通过调用生成器的下一个方法消耗值时,Generator函数将执行,直到遇到yield关键字。

可以根据需要多次调用该函数,并且每次都返回一个新的Generator,但每个Generator只能迭代一次。

所以我们可以有以下例子:

function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}
var a = makeRangeIterator(1,10,2)
a.next() // {value: 1, done: false}
a.next() // {value: 3, done: false}
a.next() // {value: 5, done: false}
a.next() // {value: 7, done: false}
a.next() // {value: 9, done: false}
a.next() // {value: undefined, done: true}

14、Set/WeakSet

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。

所以我们可以通过Set实现数组去重

const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) 
// [2, 3, 4, 5, 6, 7, 32]

WeakSet 结构与 Set 类似,但区别有以下两点:

•WeakSet 对象中只能存放对象引用, 不能存放值, 而 Set 对象都可以。•WeakSet 对象中存储的对象值都是被弱引用的, 如果没有其他的变量或属性引用这个对象值, 则这个对象值会被当成垃圾回收掉. 正因为这样, WeakSet 对象是无法被枚举的, 没有办法拿到它包含的所有元素。

所以代码如下:

var ws = new WeakSet()
var obj = {}
var foo = {}

ws.add(window)
ws.add(obj)

ws.has(window) // true
ws.has(foo)    // false, 对象 foo 并没有被添加进 ws 中 

ws.delete(window) // 从集合中删除 window 对象
ws.has(window)    // false, window 对象已经被删除了

ws.clear() // 清空整个 WeakSet 对象

15、Map/WeakMap

Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。

例子如下,我们甚至可以使用NaN来作为键值:

var myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN); // "not a number"

var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"

WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。

跟Map的区别与Set跟WeakSet的区别相似,具体代码如下:

var wm1 = new WeakMap(),
    wm2 = new WeakMap(),
    wm3 = new WeakMap();
var o1 = {},
    o2 = function(){},
    o3 = window;

wm1.set(o1, 37);
wm1.set(o2, "azerty");
wm2.set(o1, o2); // value可以是任意值,包括一个对象
wm2.set(o3, undefined);
wm2.set(wm1, wm2); // 键和值可以是任意对象,甚至另外一个WeakMap对象
wm1.get(o2); // "azerty"
wm2.get(o2); // undefined,wm2中没有o2这个键
wm2.get(o3); // undefined,值就是undefined

wm1.has(o2); // true
wm2.has(o2); // false
wm2.has(o3); // true (即使值是undefined)

wm3.set(o1, 37);
wm3.get(o1); // 37
wm3.clear();
wm3.get(o1); // undefined,wm3已被清空
wm1.has(o1);   // true
wm1.delete(o1);
wm1.has(o1);   // false

16、Proxy/Reflect

Proxy 对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 Proxy 的方法相同。Reflect不是一个函数对象,因此它是不可构造的。

Proxy跟Reflect是非常完美的配合,例子如下:

const observe = (data, callback) => {
      return new Proxy(data, {
            get(target, key) {
                return Reflect.get(target, key)
            },
            set(target, key, value, proxy) {
                  callback(key, value);
                  target[key] = value;
                    return Reflect.set(target, key, value, proxy)
            }
      })
}

const FooBar = { open: false };
const FooBarObserver = observe(FooBar, (property, value) => {
  property === 'open' && value 
          ? console.log('FooBar is open!!!') 
          : console.log('keep waiting');
});
console.log(FooBarObserver.open) // false
FooBarObserver.open = true // FooBar is open!!!

当然也不是什么都可以被代理的,如果对象带有configurable: false 跟writable: false 属性,则代理失效。

17、Regex对象的扩展

正则新增符号

i 修饰符

  // i 修饰符
  /[a-z]/i.test('\u212A') // false
  /[a-z]/iu.test('\u212A') // true

y修饰符

  // y修饰符
  var s = 'aaa_aa_a';
  var r1 = /a+/g;
  var r2 = /a+/y;

  r1.exec(s) // ["aaa"]
  r2.exec(s) // ["aaa"]

  r1.exec(s) // ["aa"]
  r2.exec(s) // null

String.prototype.flags

/

// 查看RegExp构造函数的修饰符
  var regex = new RegExp('xyz', 'i')
  regex.flags // 'i'

unicode模式

 var s = '????'
  /^.$/.test(s) // false
  /^.$/u.test(s) // true

u转义

// u转义
  /\,/ // /\,/
  /\,/u // 报错 没有u修饰符时,逗号前面的反斜杠是无效的,加了u修饰符就报错。

引用

const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/;
  RE_TWICE.test('abc!abc') // true
  RE_TWICE.test('abc!ab') // false

  const RE_TWICE = /^(?<word>[a-z]+)!\1$/;
  RE_TWICE.test('abc!abc') // true
  RE_TWICE.test('abc!ab') // false

字符串方法的实现改为调用RegExp方法

String.prototype.match 调用 RegExp.prototype[Symbol.match]•String.prototype.replace 调用 RegExp.prototype[Symbol.replace]•String.prototype.search 调用 RegExp.prototype[Symbol.search]•String.prototype.split 调用 RegExp.prototype[Symbol.split]

正则新增属性

RegExp.prototype.sticky 表示是否有y修饰符

/hello\d/y.sticky // true

RegExp.prototype.flags获取修饰符

/abc/ig.flags // 'gi'

18、Math对象的扩展

•二进制表示法 : 0b或0B开头表示二进制(0bXX或0BXX)

•二进制表示法 : 0b或0B开头表示二进制(0bXX或0BXX)

•八进制表示法 : 0o或0O开头表示二进制(0oXX或0OXX)

•Number.EPSILON : 数值最小精度

•Number.MIN_SAFE_INTEGER : 最小安全数值(-2^53)

•Number.MAX_SAFE_INTEGER : 最大安全数值(2^53)

•Number.parseInt() : 返回转换值的整数部分

•Number.parseFloat() : 返回转换值的浮点数部分

•Number.isFinite() : 是否为有限数值

•Number.isNaN() : 是否为NaN

•Number.isInteger() : 是否为整数

•Number.isSafeInteger() : 是否在数值安全范围内

•Math.trunc() : 返回数值整数部分

•Math.sign() : 返回数值类型(正数1、负数-1、零0)

•Math.cbrt() : 返回数值立方根

•Math.clz32() : 返回数值的32位无符号整数形式

•Math.imul() : 返回两个数值相乘

•Math.fround() : 返回数值的32位单精度浮点数形式

•Math.hypot() : 返回所有数值平方和的平方根

•Math.expm1() : 返回e^n - 1

•Math.log1p() : 返回1 + n的自然对数(Math.log(1 + n))

•Math.log10() : 返回以10为底的n的对数

•Math.log2() : 返回以2为底的n的对数

•Math.sinh() : 返回n的双曲正弦

•Math.cosh() : 返回n的双曲余弦

•Math.tanh() : 返回n的双曲正切

•Math.asinh() : 返回n的反双曲正弦

•Math.acosh() : 返回n的反双曲余弦

•Math.atanh() : 返回n的反双曲正切

19、Array对象的扩展

•Array.prototype.from:转换具有Iterator接口的数据结构为真正数组,返回新数组。

console.log(Array.from('foo')) // ["f", "o", "o"]
  console.log(Array.from([1, 2, 3], x => x + x)) // [2, 4, 6]

•Array.prototype.of():转换一组值为真正数组,返回新数组。

 Array.of(7)       // [7] 
  Array.of(1, 2, 3) // [1, 2, 3]

  Array(7)          // [empty, empty, empty, empty, empty, empty]
  Array(1, 2, 3)    // [1, 2, 3]

•Array.prototype.copyWithin():把指定位置的成员复制到其他位置,返回原数组

 const array1 = ['a', 'b', 'c', 'd', 'e']

  console.log(array1.copyWithin(0, 3, 4)) // ["d", "b", "c", "d", "e"]

  console.log(array1.copyWithin(1, 3)) // ["d", "d", "e", "d", "e"]

•Array.prototype.find():返回第一个符合条件的成员

const array1 = [5, 12, 8, 130, 44]

  const found = array1.find(element => element > 10)

  console.log(found) // 12

•Array.prototype.findIndex():返回第一个符合条件的成员索引值

  const array1 = [5, 12, 8, 130, 44]

  const isLargeNumber = (element) => element > 13

  console.log(array1.findIndex(isLargeNumber)) // 3

•Array.prototype.fill():根据指定值填充整个数组,返回原数组

 const array1 = [1, 2, 3, 4]

  console.log(array1.fill(0, 2, 4)) // [1, 2, 0, 0]

  console.log(array1.fill(5, 1)) // [1, 5, 5, 5]

  console.log(array1.fill(6)) // [6, 6, 6, 6]

•Array.prototype.keys():返回以索引值为遍历器的对象

 const array1 = ['a', 'b', 'c']
  const iterator = array1.keys()

  for (const key of iterator) {
        console.log(key)
  }

  // 0
  // 1
  // 2

•Array.prototype.values():返回以属性值为遍历器的对象

  const array1 = ['a', 'b', 'c']
  const iterator = array1.values()

  for (const key of iterator) {
        console.log(key)
  }

  // a
  // b
  // c

•Array.prototype.entries():返回以索引值和属性值为遍历器的对象

 const array1 = ['a', 'b', 'c']
  const iterator = array1.entries()

  console.log(iterator.next().value) // [0, "a"]
  console.log(iterator.next().value) // [1, "b"]

•数组空位:ES6明确将数组空位转为

undefined或者empty

  Array.from(['a',,'b']) // [ "a", undefined, "b" ]
  [...['a',,'b']] // [ "a", undefined, "b" ]
  Array(3) //  [empty × 3]
  [,'a'] // [empty, "a"]

ES7(ES2016)

1、Array.prototype.includes()
includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

代码如下:

const array1 = [1, 2, 3]
console.log(array1.includes(2)) // true

const pets = ['cat', 'dog', 'bat']
console.log(pets.includes('cat')) // true
console.log(pets.includes('at')) // false

2、幂运算符**

幂运算符**,具有与Math.pow()一样的功能,代码如下:

console.log(2**10) // 1024
console.log(Math.pow(2, 10)) // 1024

3、模板字符串(Template string)

自ES7起,带标签的模版字面量遵守以下转义序列的规则:

•Unicode字符以"\u"开头,例如\u00A9•Unicode码位用"\u{}“表示,例如\u{2F804}•十六进制以”\x"开头,例如\xA9•八进制以""和数字开头,例如\251

这表示类似下面这种带标签的模版是有问题的,因为对于每一个ECMAScript语法,解析器都会去查找有效的转义序列,但是只能得到这是一个形式错误的语法:

latex`\unicode`
// 在较老的ECMAScript版本中报错(ES2016及更早)
// SyntaxError: malformed Unicode character escape sequence

ES8(ES2017)

1、async/await
虽然Promise可以解决回调地狱的问题,但是链式调用太多,则会变成另一种形式的回调地狱 —— 面条地狱,所以在ES8里则出现了Promise的语法糖async/await,专门解决这个问题。

我们先看Promise代码:

fetch('coffee.jpg')
    .then(response => response.blob())
    .then(myBlob => {
          let objectURL = URL.createObjectURL(myBlob)
          let image = document.createElement('img')
          image.src = objectURL
          document.body.appendChild(image)
    })
    .catch(e => {
          console.log('There has been a problem with your fetch operation: ' + e.message)
    })

然后再看看async/await版的,这样看起来是不是更清晰了。

async function myFetch() {
      let response = await fetch('coffee.jpg')
      let myBlob = await response.blob()

      let objectURL = URL.createObjectURL(myBlob)
      let image = document.createElement('img')
      image.src = objectURL
      document.body.appendChild(image)
}
myFetch()

当然,如果你喜欢,你甚至可以两者混用

async function myFetch() {
      let response = await fetch('coffee.jpg')
      return await response.blob()
}

myFetch().then((blob) => {
      let objectURL = URL.createObjectURL(blob)
      let image = document.createElement('img')
      image.src = objectURL
      document.body.appendChild(image)
})

2、Object.values()

Object.values()方法返回一个给定对象自身的所有可枚举属性值的数组,值的顺序与使用for…in循环的顺序相同 ( 区别在于 for-in 循环枚举原型链中的属性 )。

代码如下:

const object1 = {
      a: 'somestring',
      b: 42,
      c: false
}
console.log(Object.values(object1)) // ["somestring", 42, false]

3、Object.entries()

Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for…in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

代码如下:

const object1 = {
      a: 'somestring',
      b: 42
}

for (let [key, value] of Object.entries(object1)) {
      console.log(`${key}: ${value}`)
}

// "a: somestring"
// "b: 42"

4、padStart()

padStart() 方法用另一个字符串填充当前字符串(重复,如果需要的话),以便产生的字符串达到给定的长度。填充从当前字符串的开始(左侧)应用的。

代码如下:

const str1 = '5'
console.log(str1.padStart(2, '0')) // "05"

const fullNumber = '2034399002125581'
const last4Digits = fullNumber.slice(-4)
const maskedNumber = last4Digits.padStart(fullNumber.length, '*') 
console.log(maskedNumber) // "************5581"

5、padEnd()

padEnd() 方法会用一个字符串填充当前字符串(如果需要的话则重复填充),返回填充后达到指定长度的字符串。从当前字符串的末尾(右侧)开始填充。

const str1 = 'Breaded Mushrooms'
console.log(str1.padEnd(25, '.')) // "Breaded Mushrooms........"
const str2 = '200'
console.log(str2.padEnd(5)) // "200  "

6、Atomics对象

Atomics对象 提供了一组静态方法用来对 SharedArrayBuffer[3] 对象进行原子操作。

方法如下:

•Atomics.add() :将指定位置上的数组元素与给定的值相加,并返回相加前该元素的值。

•Atomics.and():将指定位置上的数组元素与给定的值相与,并返回与操作前该元素的值。

•Atomics.compareExchange():如果数组中指定的元素与给定的值相等,则将其更新为新的值,并返回该元素原先的值。

•Atomics.exchange():将数组中指定的元素更新为给定的值,并返回该元素更新前的值。

•Atomics.load():返回数组中指定元素的值。

•Atomics.or():将指定位置上的数组元素与给定的值相或,并返回或操作前该元素的值。

•Atomics.store():将数组中指定的元素设置为给定的值,并返回该值。

•Atomics.sub():将指定位置上的数组元素与给定的值相减,并返回相减前该元素的值。

•Atomics.xor():将指定位置上的数组元素与给定的值相异或,并返回异或操作前该元素的值。

•Atomics.wait():检测数组中某个指定位置上的值是否仍然是给定值,是则保持挂起直到被唤醒或超时。返回值为 "ok""not-equal""time-out"。调用时,如果当前线程不允许阻塞,则会抛出异常(大多数浏览器都不允许在主线程中调用 wait())。

•Atomics.wake():唤醒等待队列中正在数组指定位置的元素上等待的线程。返回值为成功唤醒的线程数量。

•Atomics.isLockFree(size):可以用来检测当前系统是否支持硬件级的原子操作。对于指定大小的数组,如果当前系统支持硬件级的原子操作,则返回 true;否则就意味着对于该数组,Atomics 对象中的各原子操作都只能用锁来实现。此函数面向的是技术专家。

7、Object.getOwnPropertyDescriptors()

Object.getOwnPropertyDescriptors() 方法用来获取一个对象的所有自身属性的描述符。代码如下:

const object1 = {
  property1: 42
}

const descriptors1 = Object.getOwnPropertyDescriptors(object1)

console.log(descriptors1.property1.writable) // true

console.log(descriptors1.property1.value) // 42

// 浅拷贝一个对象
Object.create(
  Object.getPrototypeOf(obj), 
  Object.getOwnPropertyDescriptors(obj) 
)

// 创建子类
function superclass() {}
superclass.prototype = {
  // 在这里定义方法和属性
}
function subclass() {}
subclass.prototype = Object.create(superclass.prototype, Object.getOwnPropertyDescriptors({
  // 在这里定义方法和属性
}))

ES9(ES2018)

1、for await…of
for await...of 语句会在异步或者同步可迭代对象上创建一个迭代循环,包括 String,Array,Array-like 对象(比如arguments 或者NodeList),TypedArray,Map, Set和自定义的异步或者同步可迭代对象。其会调用自定义迭代钩子,并为每个不同属性的值执行语句。

配合迭代异步生成器,例子如下:

async function* asyncGenerator() {
      var i = 0
      while (i < 3) {
            yield i++
      }
}

(async function() {
      for await (num of asyncGenerator()) {
            console.log(num)
      }
})()
// 0
// 1
// 2

2、模板字符串(Template string)

ES9开始,模板字符串允许嵌套支持常见转义序列,移除对ECMAScript在带标签的模版字符串中转义序列的语法限制。

不过,非法转义序列在"cooked"当中仍然会体现出来。它们将以undefined元素的形式存在于"cooked"之中,代码如下:

function latex(str) { 
 return { "cooked": str[0], "raw": str.raw[0] }
} 

latex`\unicode` // { cooked: undefined, raw: "\\unicode" }

3、对象扩展操作符

ES6中添加了数组的扩展操作符,让我们在操作数组时更加简便,美中不足的是并不支持对象扩展操作符,但是在ES9开始,这一功能也得到了支持,例如:

var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };
// 克隆后的对象: { foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };
// 合并后的对象: { foo: "baz", x: 42, y: 13 }

上面便是一个简便的浅拷贝。这里有一点小提示,就是Object.assign() 函数会触发 setters,而展开语法则不会。所以不能替换也不能模拟Object.assign() 。

如果存在相同的属性名,只有最后一个会生效。
4、Promise.prototype.finally()

finally()方法会返回一个Promise,当promise的状态变更,不管是变成rejected或者fulfilled,最终都会执行finally()的回调。
例子如下:

fetch(url)
      .then((res) => {
        console.log(res)
      })
      .catch((error) => { 
        console.log(error)
      })
      .finally(() => { 
        console.log('结束')
    })

暂时到这,以后再继续更新