ES6 ~ ES12 部分新特性整理

3,296 阅读11分钟

本文带来部分 ES6 ~ ES12 分享,会持续更新

es6 是更新比较大的一个版本,之后每年都会有一个新的版本(2015 年出的 es6,所以在年份上 +1 就是最新的 es 版本) es6 官方文档(英文)

废话少说,直接开始,其中缺少的部分会慢慢的全部补上

ES6(2015)


let 和 const

  块级作用域
  声明的对象不在 window/global 下,在 script 下

箭头函数

  1. 没有 arguments,可以用剩余参数来代替
    没有 arguments,可以用剩余参数来代替
    const breeze = (...args) => {
      console.log(args[1])
    }
    breeze(1, 2, 3) // 2
  1. 箭头函数不会改变 this 指向,也就是箭头函数没有自己的 this 指向

    • 普通函数

      • 如果该函数是一个构造函数,this 指针指向一个新的对象
      • 在严格模式下的函数调用下,this 指向 undefined
      • 如果该函数是一个对象的方法,则它的 this 指针指向这个对象

字符串

  模板字符串
  支持 for of
  includes(): 判断是否存在字符串
  startsWith(): 判断字符串开头部分
  endsWith(): 判断字符串结束部分
  repeat(): 将字符串重复几次

数值

  Number.MIN_SAFE_INTEGER: 最小安全数(-2^53)
  Number.MAX_SAFE_INTEGER: 最大安全数(2^53)
  Number.isSafeInteger(): 是否在安全数内
  Number.isNaN()/isNaN(): 是否为 NaN // 区别(isNaN 先调用 Number())
  Math.sign(): 数值类型(返回值:正数 1,负数 -1,零 0Math.imul(): 两数相乘

对象

  for in: 遍历可枚举、可继承属性
  Object.keys(): 可枚举属性
  Object.is(): 和 "===" 用法基本相同,区别是 +0 不等于 -0,NaN 等于自身
  Object.assign(): 合并对象(浅拷贝)
  Object.create(): 新建对象,继承原型链,用于实现继承的方法
  function Parent () {
    this.name = 'breeze'
  }
  function Child () {
    Parent.call(this)
    this.address = 'shanghai'
  }
  Child.prototype = Object.create(Parent.prototype)
  Child.prototype.constructor = Child // 注意这边的构造函数,如果不加的话还是 Parent
  let breeze = new Child();

数组

  扩展运算符:
    数组合并,
    克隆数组,
    转换字符串为数组,
    转换类数组为数组(arguments),
    转换可迭代对象为数组(SetMapString)(拥有 Symbol.iteratorArray.from(): 将类数组、可迭代对象转为数组
  Array.of(): 将一组值,转为数组
  fill(): 填充数组(返回原数组)

解构赋值

  // 对象、数组解构
  [x, y] = [y, x]
  const [x, y = 2] = [1] // 默认值(目标值为 undefined 的时候生效)
  const { a, b: c } = { a: 1, b: 2 } // 重命名
  const [a, , c] = [1, 2, 3] // 忽略某些值
  const [a, ...b] = [1, 2, 3, 4]; // b: [2, 3, 4]
  const [a, b] = [1, 2]
  const [a, b = 2] = [1]
  const [a, b, c, d, e, f] = "breeze" // 字符串解构
  // 数值和布尔值会转成包装对象,可以解构出其中原型对象上的属性方法
  const { toString: breeze } = 666 // 数值解构,可重命名
  const { toString: breeze } = false // 布尔解构

Symbol

  声明方式: Symbol('') // 不需要 new,不属于引用类型
  独一无二的值,即使是相同的参数,不可被 for inObject.keys 访问,可以用作私有属性的创建
  Symbol.for() // 也可以创建,相同参数创建的值相同
  可以用 String 或者 toString 转为字符串类型
  Symbol.iterator: 指向默认遍历器的方法,当用到 for of 的时候会去调用指定的默认遍历器
  部署 Symbol.iterator 了的有:
    1.Array
    2.Map
    3.Set
    4.String
    5.arguments 对象
    6.Nodelist 对象, 获取的 dom 列表集合

Set

  声明: new Set() // 参数为拥有 Iterator 接口的数据结构
  去重数组、字符串: new Set([])

  方法:
  size: 长度
  add(): 添加值,返实例
  delete(): 删除值,返布尔
  has(): 检查值,返布尔
  clear(): 清除
  keys(): 返回属性值的对象,可用 for of 遍历
  values(): 返回属性值的对象,可用 for of 遍历
  entries(): 返回 key -> value 结构的对象
  forEach(): 遍历

WeakSet

  成员只能是对象
  没有 Symbol.iterator,所以不可用 for of 和 forEach
  成员都是弱引用,可以被垃圾回收机制回收
  方法:
  add(): 添加值,返实例
  delete(): 删除值,返布尔
  has(): 判断是否存在,返布尔

Map

  声明方式: new Map([[], []]) // Iterator接口,成员都是一个双元素数组的数据结构
  任意值做 key,比如对象,可以解决的是同名属性冲突的问题(注:在 Map 和 Set 中 NaN 视为同一个值)
  方法:
  size: 长度
  get(key): 通过键查找特定的数值并返回
  set(key, value): 添加,返实例
  delete(key): 删除,返布尔
  has(key): 判断是否存在,返布尔
  clear(): 清除所有成员

  keys(): 返回以 key 为值的对象
  values(): 返回以 value 为值得对象
  entries(): 返回以 key->value 的对象,和原来结构类似
  forEach(): 遍历

WeakMap

  键只能是引用类型,并且是弱引用,值可以为任意的值;不可遍历;
  get():返回值
  set():添加,返实例
  delete():删除,返布尔
  has():判断是否存在,返布尔3

Map、Set、WeakMap、WeakSet 对比以及用处

  什么是弱引用,以下为例
  let obj = { name: 'breeze' };
  obj = null;
  垃圾回收机制,只要地址不再被引用就会被自动清除
  WeakMap所构建的实例中,其key键所对应引用地址的引用断开或不属于指向同一个内存地址的时候,其对应value值就会被加入垃圾回收队伍

  同样大小的内存,map 比 Object 存的多,所以 Map 胜,
  插入时 插入时两种方式差不多,插入大量数据时,还是选择 map
  查找速度 差异极小,使用连续整数作为属性时,调用浏览器可以进行优化,可能速度会高点
  删除性能 删除时,map 更优
  用处:
  1.在不修改原有对象的情况下,想要根据对象保存某些里面的值得时候可以用到 WeakMap,一旦这个对象不存在,自动回收
  // 比如说保存对象属性的长度的缓存
  const breeze = new WeakMap()

  function fun(obj) {
    // 如果当前缓存中有这个数据就直接返回
    if(breeze.has(obj)) {
      return breeze.get(obj)
    } else {
      // 如果没有,就把当前对象的键的长度存在缓存中
      const count = Object.keys(obj).length
      breeze.set(obj, count)
      // 返回这次计算的长度
      return count
    }
  }
  2.如果需要对 dom 节点关联一些数据,比如是否禁用,可以用映射使他们关联起来,但是如果有一些操作可能需要删除掉这个 dom 节点,这个时候如果用 map 的话,dom 节点虽然删除了,但是他仍然在 map 中被引用着,所以耗费内存,这时,使用 weakMap 的弱引用就能被自动回收
  let breeze = new WeakMap()
  const btn = document.querySelector('#btn')
  // 给 btn 节点添加一些关联数据等
  breeze.set(btn, { disabled: true })
  3.解决深拷贝中循环引用的问题
  let obj1 = {};
  let obj2 = {};
  obj1.name = obj2;
  obj2.name = obj1;
  let weakMap = new WeakMap();
  function deepClone(obj) {
    if (obj === null) {
      return obj
    }
    if (typeof obj !== 'object') {
      return obj
    }
    if (obj.constructor === Date) return new Date(obj);
    if (weakMap.has(obj)) { return weakMap.get(obj) };
    let newObj = new obj.constructor(); // 拿到原型对象上的属性
    weakMap.set(obj, newObj);
    for (key in obj) {
      if (obj.hasOwnProperty(key)) {
        newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key] // 此处可以用 arguments.callee() 实现
      }
    }
    return newObj;
  }

函数参数默认值

对象属性简写

类(class)

  class Breeze {
    constructor(value) {
      const key = Symbol();
      this.name = value;
      this[key] = '';
    }
    fun () {
      console.log('')
    }
    static staticMethod() {
      console.log('只能通过类调用')
    }
    set age (value) {
      this.name = value;
    }
    get age () {
      return this.name;
    }
  }
  class SubBreeze extends Breeze {
    constructor(value) {
      super(''); // 调用父类的构造函数
      this.title = value;
    }
  }

模块化

promise

Generator

Proxy

  new Proxy(target, handler); // target 表示需要拦截的对象,handler 也是对象,拦截的行为
  let obj = { name: 'breeze' };
  let proxy = new Proxy(obj, {
    get (obj, key, proxy) {
      return obj[key];
    },
    set (obj, key, value, proxy) {
      obj[key] = value;
    },
    has (obj, key) { // 可以隐藏属性,不被 in 发现
      if (key === name) {
        return false;
      }
      return key in obj;
    },
    ownKeys(obj) { // 可以
      return Reflect.ownKeys(obj).filter(
        value => value !== 'name'
      )
    },
  })
  deleteProperty(item, propKey): 拦截操作 delete proxy[propKey] , 返回一个布尔值
  ownKeys(item): 拦截操作,比如 Object.getOwnPropertyNames(proxy), Object.getOwnPropertySymbols(proxy), Object.keys(proxy), for...in, 返回一个数组。
  创建新的实例,必须修改实例才会触发 handler 中的行为
  十三种拦截操作:
  1.get():拦截属性读取
  2.set():拦截属性设置,返回布尔
  3.has():拦截属性检查,返回布尔
  4.deleteProperty():拦截属性删除,返回布尔
  5.defineProperty():拦截属性定义 Object.defineProperty()、Object.defineProperties(),返回布尔
  6.ownKeys():拦截属性遍历for-in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols(),返回数组
  7.getOwnPropertyDescriptor():拦截属性描述读取 Object.getOwnPropertyDescriptor(),返回对象
  8.getPrototypeOf():拦截原型读取instanceof、Object.getPrototypeOf()、Object.prototype.__proto__、Object.prototype.isPrototypeOf()、Reflect.getPrototypeOf(),返回对象
  9.setPrototypeOf():拦截原型设置 Object.setPrototypeOf(),返回布尔
  10.isExtensible():拦截是否可扩展读取 Object.isExtensible(),返回布尔
  11.preventExtensions():拦截不可扩展设置Object.preventExtensions(),返回布尔
  12.apply():拦截 Proxy 实例作为函数调用 proxy()、proxy.apply()、proxy.call()
  13.construct():拦截 Proxy 实例作为构造函数调用 new proxy()
  Object.defineProperty(obj, prop, descriptor)
  let obj = {};
  let value = 'breeze'
  Object.defineProperty(obj, "name", {
    value : 'breeze', // 属性的值
    writable : true, // 为 true 时才能被赋值运算符改变,默认为 false。
    enumerable : true, // 为 true 时候是可枚举的,默认为 false
    configurable : true // 为 true 时才能够被改变,也能够被删除,默认 false
    get() {
      return value; // 获取值的时候执行
    },
    set(newValue) {
      value = newValue; // 设置值的时候执行,newValue 为设置的值
    }
  });

Reflect

  Reflect 是一个内建的对象,用来提供方法去拦截 js 的操作。
  Reflect 是不可构造的,不能通过 new 去新建,Reflect 的所有属性和方法都是静态的。
  将 Object 属于语言内部的方法放到 Reflect,让其操作变成函数行为
  ProxyReflect相辅相成,方法是一一对应的,ProxyReflect 联合使用,前者负责拦截赋值操作,后者负责完成赋值操作
  方法:
  1.get(obj, key):返回对象属性
  2.set(obj, key, value):设置对象属性,返回布尔
  3.has(obj, key):检查对象属性,返回布尔
  4.deleteProperty(obj, key):删除对象属性,返回布尔
  5.defineProperty():定义对象属性,返回布尔
  6.ownKeys():遍历对象属性,返回数组(Object.getOwnPropertyNames() + Object.getOwnPropertySymbols())
  7.getOwnPropertyDescriptor(obj, key):返回对象属性描述,返回对象
  8.getPrototypeOf(obj):返回对象原型,返回对象
  9.setPrototypeOf(obj, prototype):设置对象原型,返回布尔
  10.apply(target, thisArgument, argumentsList):绑定 this 后执行指定函数
  target 为目标函数,
  thisArgument 为需要指定 this 的对象,
  argumentsList 为目标函数调用时传入的实参列表,应该是一个类数组的对象
    let obj = {
      name: 'breeze',
      getName: function () {
        console.log(this.name)
      }
    }
    let fun = obj.getName;
    Reflect.apply(fun, obj, {})
  11.construct(target, args, newTarget):调用构造函数创建实例
  target 是构造函数,
  args 是调用构造函数传递的参数数组或伪数组,
  newTarget 是指向新的构造函数,可以用 instanceof 来判断
   function Breeze (name) {
     this.name = name;
   }
   function Wing () {};
   Wing.prototype.getName = function () {};
   let breeze = new Breeze('breeze');
   let breeze = Reflect.construct(Breeze, ['breeze'], Wing);
  注意:如果有第三个参数的话,那么实例由两部分组成,实列的属性部分由第一个参数构造函数生成,实列的方法由第三个参数对象生成。
  12.isExtensible():返回对象是否可扩展,返回布尔
  13.preventExtensions():设置对象不可扩展,返回布尔
    const obj = {};
    // 判断该对象是否可以扩展,使用 Reflect.isExtensible
    Reflect.isExtensible(obj); // true
    // 使用 Reflect.preventExtensions 来阻止该对象扩展
    Reflect.preventExtensions(obj);
    Reflect.isExtensible(obj); // false

  目的:简化写法
  比如:
    Object.prototype.hasOwnProperty.call(obj, 'name') -> Reflect.ownKeys(obj)
  将某些 Object 方法报错情况改成返回 false
  不同写法:
    try {
      Object.defineProperty(target, property, attributes);
    } catch(e) {
      // 失败
    }

    if (Reflect.defineProperty(target, property, attributes)) {
      // success
    } else {
      // failure
    }

ES7(2016)


includes

指数操作符

2**10; // 1024

ES8(2017)


Object.values()

Object.values({a: 1, b: 2, c: 3}); // [1, 2, 3]

Object.entries()

Object.entries({a: 1, b: 2, c: 3}); // [["a", 1], ["b", 2], ["c", 3]]

字符串填充空格 (String padding)

// 字符串开头加空格
'breeze'.padStart(10);
// 字符串结尾加空格
'breeze'.padEnd(10);

async/await 同步语法糖

Object.getOwnPropertyDescriptors()

  // 可以获取对象的自身所有属性的描述符,如果没有自身属性,返回空对象
  let obj = {
    name: 'breeze',
    [Symbol()]: 21,
  }
  console.log(Object.getOwnPropertyDescriptors(obj)) // { Symbol(): { configurable: true, enumerable: true, value: 21, writable: true } }

SharedArrayBuffer 对象

Atomics 对象

ES9(2018)


异步迭代

await 可以和 for of 循环一起使用,运行异步操作
async function breeze(arr) {
  for await (let i of arr) {
    // do(i);
  } }

Promise.finally()

  Promise.resolve().then().catch(e => e).finally();

ES10(2019)


Array.flat(deep)

  参数决定了可以扁平化到哪一个层级,没有参数的时候默认为 1,返回新的数组,对原数组不产生影响
  [1, 2, [[3, 4]]].flat(1); // [1, 2, [3, 4]]
  [1, 2, [[3, 4]]].flat(2); // [1, 2, 3, 4]
  // 无论嵌套多深,都会变成一维数组
  [1, 2, [[3, 4]]].flat(Infinity); // [1, 2, 3, 4]
  // 删除数组中的空值
  [1, , 2, 3].flat() // [1, 2, 3]

Array.flatMap()

  区别:
  [1, 2, 3, 4].flatMap(a => [a*2]); // [2, 4, 6, 8]
  [1, 2, 3, 4].map(a => [a*2]); // [[2], [4], [6], [8]]

String.trimStart() 和 String.trimEnd()

去除字符串首尾空白字符

Object.fromEntries()

返回一个给定对象自身可枚举属性的键值对数组
// 通过 Object.fromEntries, 将 Map 转为 Object:
const map = new Map([ ['a', 1], ['b', 2] ]);
console.log(Object.fromEntries(map)); // { a: 1, b: 2 }

ES11(2020)


?? 空值处理

  ?? 左侧的值必须是 undefined 或者 null 的时候,才会返回返右侧的值,否则返回左侧的值。
  let breeze = {
    a: 0,
    b: false,
    c: null,
    d: undefined,
    e: 'ceshi',
  }
  let a = breeze.a ?? '1' // 0
  let b = breeze.b ?? '2' // false
  let c = breeze.c ?? '3' // 3
  let d = breeze.d ?? '4' // 4
  let e = breeze.e ?? '5' // 'ceshi'

?. 可选链 判断是否存在

  ?. 判断不确定的中间节点,代替 && 节省代码空间
  let breeze = {}
  let name = breeze.people.name // TypeError: Cannot read property 'name' of undefined
  let color = breeze.people?.color // undefined

基本数据类型 BigInt

BigInt(value)
可以表示任意大的整数
任意精度的整数

ES12(2021)


replaceAll

  返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
  const breeze = 'breeze';
  breeze.replaceAll('e', 'k'); // "brkkzk"

数字分隔符

  数字分隔符,可以在数字之间创建可视化分隔符,通过_下划线来分割数字,使数字更具可读性
  const money = 1_000_000_000;
  //等价于
  const money = 1000000000;
  1_000_000_000 === 1000000000; // true

WeakRefs