Skip to content

一. ES7新增特性


1. Array.prototype.includes()

  • ES7之前,如果我们想判断一个数组中是否包含某个元素,需要通过indexOf 获取结果,并且判断是否为-1

  • ES7中,我们可以通过includes来判断一个数组中是否包含一个指定的元素,根据情况,如果包含则返回true,否则返回false

  • Array.prototype.includes(searchElement, fromIndex)

    • searchElement:需要查找的元素值
    • fromIndex(可选):从fromIndex 索引处开始查找 valueToFind,默认为0
    js
    const names = ['aaa', 'bbb', 'later']
    
    names.indexOf('later') // 2
    names.includes('later') // true
    names.includes('ccc') // false

2. 指数exponentiation运算符

  • ES7之前,计算数字的乘方需要通过Math.pow()方法来完成

  • ES7中,增加了**运算符,可以对数字来计算乘方

    js
    // es7之前
    Math.pow(3, 2) // 9
    
    // es7
    3**2 // 9

二. ES8新增特性


1. Object.values()

  • 之前我们可以通过Object.keys()获取一个对象所有的key(除Symbol以外)

  • ES8中提供了Object.values()来获取所有的value值(除Symbol以外):

    js
    const obj = {
      name: 'later',
      age: 18,
      height: 1.88,
      [Symbol('aaa')]: 'ccc'
    }
    
    obj // {name: 'later', age: 18, height: 1.88, Symbol(aaa): 'ccc'}
    Object.keys(obj) // ['name', 'age', 'height']
    Object.values(obj) // ['later', 18, 1.88]
    Object.getOwnPropertySymbols(obj) // [Symbol(aaa)]
    
    Object.values('abc') // ['a', 'b', 'c']
    Object.keys('abc') // ['0', '1', '2']

2. Object.entries()

  • 通过Object.entries()可以获取到一个数组,数组中会存放可枚举属性的键值对数组

    • 可以针对对象、数组、字符串进行操作,返回一个数组
    js
    const obj = {
      name: 'later',
      age: 18,
      height: 1.88,
      [Symbol('aaa')]: 'ccc'
    }
    
    Object.entries(obj) // [['name', 'later'], ['age', 18], ['height', 1.88]]
    for (const item of Object.entries(obj)) {
      const [key, value] = item
      console.log(key, value)
    }
    // name later
    // age 18
    // height 1.88
    
    // 数组
    Object.entries(['a', 'b']) // [['0', 'a'], ['1', 'b']]
    // 字符串
    Object.entries('abc') // [['0', 'a'], ['1', 'b'], ['2', 'c']]

3. String.prototype.padStart()、padEnd()

  • 某些字符串我们需要对其进行前后的填充,来实现某种格式化效果

  • ES8中增加了padStart() padEnd() 方法,分别是对字符串的首尾进行填充

    js
    const minute = '5'
    const second = '6'
    // 应用场景一:对时间进行格式化
    console.log(minute.padStart(2, '0'), ":", second.padStart(2, '0')) // 05 : 06
  • 我们简单举一个应用场景:比如需要对身份证、银行卡的前面位数进行隐藏:

    js
    const minute = '5'
    const second = '6'
    // 应用场景二:对一些敏感数据进行脱敏处理
    let cardNum = '430521199910102233'
    cardNum.slice(-4).padStart(18, '*') // **************2233

4. Trailing Commas

  • ES8中,我们允许在函数定义和调用时多加一个逗号:

    • 早期这种写法是不会被支持的,不推荐使用,了解即可
    js
    function foo(a, b,) {
      console.log(a, b)
    }
    
    foo(1, 2,)

5. Object.getOwnPropertyDescriptors()

  • Object.getOwnPropertyDescriptors():
    • 用来获取一个对象的所有自身属性的描述符
  • Async Function:async、await
    • 后续讲Promise讲解

三. ES9新增特性


  • Async iterators:后续迭代器讲解
  • Object spread operators:就是允许在构造对象字面量时,使用展开语法对某个对象进行语法层面的展开
  • Promise finally:后续讲Promise讲解

四. ES10新增特性


1. Array.prototype.flat()、flatMap()

  • flat()方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

    js
    // flat的使用: 
    const nums = [10, 20, [111, 222], [333, 444], [[123, 321], [231, 312]]]
    const newNums1 = nums.flat(1) 
    console.log(newNums1) // [10, 20, 111, 222, 333, 444, [123, 321], [231, 312]]
    
    const newNums2 = nums.flat(2)
    console.log(newNums2) // [10, 20, 111, 222, 333, 444, 123, 321, 231, 312]
  • flatMap()方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组

    • 注意一:flatMap是先进行map操作,再做flat的操作
    • 注意二:flatMap中的flat相当于深度为1
    js
    // flatMap的使用:
    // 对数组中每一个元素应用一次传入的map对应的函数
    const messages = [
      "Hello World aaaaa",
      "Hello Coderwhy",
      "你好啊 李银河"
    ]
    
    // 1.for循环的方式:
    let newInfos = []
    for (const item of messages) {
      const infos = item.split(" ")
      for (const info of infos) {
        newInfos.push(info)
      }
      newInfos = [...newInfos, ...infos]
      // newInfos = newInfos.concat(infos)
    }
    console.log(newInfos) // ['Hello', 'World', 'aaaaa', 'Hello', 'Coderwhy', '你好啊', '李银河']
    
    // 2.先进行map, 再进行flat操作
    const newMessages = messages.map(item => item.split(" "))
    const finalMessages = newMessages.flat(1)
    console.log(finalMessages) // ['Hello', 'World', 'aaaaa', 'Hello', 'Coderwhy', '你好啊', '李银河']
    
    // 3.flatMap
    const finalMessages = messages.flatMap(item => item.split(" "))
    console.log(finalMessages)  // ['Hello', 'World', 'aaaaa', 'Hello', 'Coderwhy', '你好啊', '李银河']

2. Object.fromEntries()

  • 在前面,我们可以通过Object.entries()将一个对象转换成自身可枚举属性的键值对数组

  • 那么如果我们有一个entries了,如何将其转换成对象呢?

    • ES10提供了Object.formEntries()来完成转换
  • 那么这个方法有什么应用场景呢?

    js
    // 1.对象
    const obj = {
      name: "why",
      age: 18,
      height: 1.88
    }
    
    const entries = Object.entries(obj)
    const info = Object.fromEntries(entries)
    console.log(info) // {name: 'why', age: 18, height: 1.88}
    
    // 2.应用
    const searchString = "?name=later&age=18&height=1.88"
    const params = new URLSearchParams(searchString)
    console.log(params.get("name")) // later
    console.log(params.get("age")) // 18
    console.log(params.entries()) // Iterator {}
    
    for (const item of params.entries()) {
      console.log(item)
    }
    // ['name', 'later']
    // ['age', '18']
    // ['height', '1.88']
    
    const paramObj = Object.fromEntries(params)
    console.log(paramObj) // {name: 'later', age: '18', height: '1.88'}

3. trimStart、trimEnd

  • 去除一个字符串首尾的空格,我们可以通过trim方法,如果单独去除前面或者后面呢?

    • ES10中给我们提供了trimStarttrimEnd
    js
    const msg = ' hello world '
    
    msg.trimStart() // 'hello world '
    msg.trimEnd() // ' hello world'

4. 其他知识点

  • Symbol description:已经讲过了
  • Optional catch binding:后面讲解try...cach讲解

五. ES11新增特性


1. BigInt

  • 在早期的JavaScript中,我们不能正确的表示过大的数字:

    • 大于MAX_SAFE_INTEGER的数值,表示的可能是不正确的

      js
      Number.MAX_SAFE_INTEGER // 9007199254740991
      const num1 = 9007199254740991 + 1
      const num2 = 9007199254740991 + 2
      console.log(num1, num2) // 9007199254740992 9007199254740992
  • 那么ES11中,引入了新的数据类型BigInt,用于表示大的整数:

    • BitInt的表示方法是在数值的后面加上n

      js
      Number.MAX_SAFE_INTEGER // 9007199254740991
      const num1 = 9007199254740992n
      const num2 = 9007199254740993n
      console.log(num1, num2) // 9007199254740992n 9007199254740993n

2. Nullish Coalescing Operator

  • ES11Nullish Coalescing Operator增加了空值合并操作符:

    • ||:只要值为假,一律使用默认值
    • ??只有值为nullundefined的时候,才会使用默认值
    js
    // '||'逻辑或 只要值为假,一律使用默认值
    let info = ''
    // info = info || "默认值" 
    // console.log(info) // '默认值'
    
    // ??: 空值合并运算符
    info = info ?? "默认值"
    console.log(info) // ''

3. Optional chaining 可选链

  • 可选链也是ES11中新增一个特性,主要作用是让我们的代码在进行nullundefined判断时更加清晰和简洁:

    • obj?.prop
    • obj?.[expr]
    • arr?.[index]
    • func?.(args)
    js
    const obj = {
      name: "why",
      friend: {
        name: "kobe",
        // running: function() {
        //   console.log("running~")
        // }
      }
    }
    
    // 1.直接调用: 非常危险
    // obj.friend.running()
    
    // 2.if判断,&&逻辑与: 麻烦/不够简洁
    // if (obj.friend && obj.friend.running) {
    //   obj.friend.running()
    // }
    
    // obj&&obj.friend&&obj.friend.running&&obj.friend.running()
    
    // 3.可选链的用法: ?.
    obj?.friend?.running?.()

4. globalThis关键字

  • 在之前我们希望获取JavaScript环境的全局对象,不同的环境获取的方式是不一样的

    • 比如在浏览器中可以通过thiswindow来获取
    • 比如在Node中我们需要通过global来获取
  • ES11中对获取全局对象进行了统一的规范:globalThis

    js
    console.log(window) // 浏览器上
    console.log(global) // Node中
    console.log(globalThis == window) // true

5. for .. in 标准化

  • ES11之前,虽然很多浏览器支持for...in来遍历对象类型,但是并没有被ECMA标准化

  • ES11中,对其进行了标准化,for...in是用于遍历对象的key的:

    js
    const obj = {
      name: 'later',
      age: 18
    }
    
    for (const key in obj) {
      console.log(key) 
    }
    // name age

6. 其他知识点

  • Dynamic Import:后续ES Module模块化中讲解
  • Promise.allSettled:后续讲Promise的时候讲解
  • import meta:后续ES Module模块化中讲解

六. ES12新增特性


1. FinalizationRegister

  • FinalizationRegistry 对象可以让你在对象被垃圾回收时请求一个回调

    • FinalizationRegistry提供了这样的一种方法:当一个在注册表中注册的对象被回收时,请求在某个时间点上调用一个清理回调。(清理回调有时被称为finalizer
    • 你可以通过调用register方法,注册任何你想要清理回调的对象,传入该对象和所含的值
      • register()方法用FinalizationRegistry实例注册一个对象,这样如果对象被垃圾回收,注册表的回调函数就会被调用
    • registry不保留对注册对象的强引用,因为这将破坏目的(如果注册中心保留强引用,对象将永远不会被回收)
    • 如果对象被回收,你的清理回调可能会在某些时候调用你为它提供的持有值(registry方法所传入的第二个参数)。被保存的值可以是任何你喜欢的值:一个原始值或者一个对象,甚至是undefined的。如果持有的值是一个对象,registry保持对它的强引用(所以它可以把它传递给你的清理回调)
  • 验证WeakSet、WeatMap的弱引用

    js
    let obj = { name: "later", age: 18 }
    
    const finalRegistry = new FinalizationRegistry((value) => {
      console.log("某一个对象被回收了:", value)
    })
    
    finalRegistry.register(obj, 'later')
    obj = null
    
    
    let obj2 = {}
    const weakSet = new WeakSet()
    weakSet.add(obj2) // 弱引用 会被回收掉
    // const set = new Set()
    // set.add(obj2) // 强引用 不会被回收
    finalRegistry.register(obj2, 'obj2')
    
    obj2 = null
    
    let obj3 = {}
    const weakMap = new WeakMap()
    weakMap.set(obj3, 'obj3') // 弱引用 会被回收掉
    // const map = new Map()
    // map.set(obj3, 'obj3') // 强引用 不会被回收
    finalRegistry.register(obj3, 'obj3')
    
    obj3 = null

2. WeakRef

  • 如果我们默认将一个对象赋值给另外一个引用,那么这个引用是一个强引用:

    • 如果我们希望是一个弱引用的话,可以使用WeakRef
    • WeakRef对象允许您保留对另一个对象的弱引用,而不会阻止被弱引用对象被GC回收
    js
    let obj = { name: 'later' }
    // let obj2 = obj // 这里产生强引用,即使Obj=null,{}也不会被回收
    
    const objWeakRef = new WeakRef(obj)
    // console.log(objWeakRef.deref()) // 这里不会被回收,mdn上解释有做特殊处理
    console.log(objWeakRef.deref().name) // later
    
    
    const finalRegistry = new FinalizationRegistry(value => {
      console.log(value, '对象被回收了')
    })
    
    finalRegistry.register(obj, 'obj')
    
    obj = null

3. logical assignment operators

  • 逻辑赋值运算符

    • a ||= ba = a || b
    • a ??= ba = a ?? b
    • a &&= ba = a && b
    js
    // 赋值运算符
    // const foo = "foo"
    let counter = 100
    counter = counter + 100
    counter += 50
    
    // 逻辑赋值运算符
    function foo(message) {
      // 1.||逻辑赋值运算符
      // message = message || "默认值"
      // message ||= "默认值" // 就是上面这种写法的另外一种简写吧(个人认为就是语法糖)
    
      // 2.??逻辑赋值运算符
      // message = message ?? "默认值"
      message ??= "默认值" // 就是上面这种写法的另外一种简写吧(个人认为就是语法糖)
    
      console.log(message)
    }
    
    foo("abc") // abc
    foo() // 默认值
    
    // 3.&&逻辑赋值运算符
    let obj = {
      name: "later",
      running: function() {
        console.log("running~")
      }
    }
    
    // 3.1.&&一般的应用场景
    // obj && obj.running && obj.running()
    // obj = obj && obj.name
    obj &&= obj.name // 就是上面这种写法的另外一种简写吧(个人认为就是语法糖)
    console.log(obj) // later

4. 其他知识点

  • Numeric Separator:讲过了

    js
    const num1 = 100_200_3000
  • String.prototype.replaceAll:字符串替换

    js
    const str1 = 'later later Later'
    // 区分大小写
    str1.replaceAll('later', 'aaa') // aaa aaa Later
    str1.replaceAll(/later/ig, 'bbb') // bbb bbb bbb

七. ES13新增特性


1. String / Array.prototype.at()

  • 前面我们有学过字符串、数组的at方法,它们是作为ES13中的新特性加入的:

    • -0 等价于 0
    js
    // 数组
    const arr = [1, 2, 3]
    console.log(arr.at(0), arr.at(-1)) // 1 3
    
    // 字符串
    const str = '123'
    console.log(str.at(0), str.at(-1)) // 1 3

2. Object.hasOwn(obj, propKey)

  • Object构造函数中新增了一个静态方法(类方法): hasOwn(obj, propKey)

    • 该方法用于判断一个对象中是否有某个自己的属性
    • Object.hasOwn()是用来替换Object.prototype.hasOwnProperty()
    image-20220704143452565
  • 那么和之前学习的Object.prototype.hasOwnProperty有什么区别呢?

    • 区别一:防止对象内部有重写hasOwnProperty
      • 因为hasOwnProperty方法是基于实例对象的原型对象进行调用的,如果对象内部自己有重写一个hasOwnProperty,就会优先调用自己的
    • 区别二:对于隐式原型指向null的对象,hasOwnProperty无法进行判断
      • 因为hasOwnProperty方法是存在于Object构造函数的显式原型上的,如果某个对象隐式原型指向null,而不是Object.prototypenull上是不存在hasOwnProperty方法的

3. New members of classes

  • ES13中,新增了定义class类中成员字段(field)的其他方式:

    • Instance public fields: 实例公共字段
    • Static public fields:静态公共字段
    • Instance private fields:实例私有字段
    • static private fields:静态私有字段
    • static block:静态代码块
    js
    class Person {
      // 1.实例属性
      // 对象属性: public 公共 -> public instance fields
      height = 1.88
      foo = () => {}
    
      // 对象属性: private 私有: 程序员之间的约定
      // _intro = "name is why"
    
      // ES13对象属性: private 私有属性 只能在class内部使用
      #intro = "hello"
    
      // 2.类属性(static)
      // 类属性: public
      static totalCount = "70亿"
    
      // 类属性: private 私有类方法 只能在class内部使用
      static #maleTotalCount = "20亿"
    
      constructor(name, age) {
        // 对象中的属性: 在constructor通过this设置
        this.name = name
        this.age = age
      }
    
      // 3.静态代码块 
      static {
        // 第一次加载解析类的代码时(初始化)执行
        console.log('类代码初始化执行')
      }
    
      run() {
        console.log(this.#intro, Person.#maleTotalCount)
      }
    }
    
    const p = new Person("later", 18)
    console.log(p)
    console.log(p.name, p.age, p.height)
    // console.log(p.#intro) // Private field '#intro' must be declared in an enclosing class
    console.log(Person.totalCount) // 70亿
    // console.log(Person.#maleTotalCount) // Private field '#intro' must be declared in an enclosing class
    p.run() // hello 20亿

Released under the MIT License.