ES6+新特性(ES2015~ES2021) ---- 持续更新中

322 阅读10分钟

前言

本人比较喜欢一些js的新特性,所以平时就喜欢关注一些新的写法,这篇文章也会持续更新,整理一些常用的、好用的api,也欢迎大家在评论区补充。。。。

ES2015

var、let、const

varletconst
是否声明提升是,并使用undefined初始化是,未初始化是,未初始化
作用域全局、函数作用域块级作用域块级作用域
初始化可以仅声明不初始化可以仅声明不初始化声明时必须初始化
是否可以重复定义
是否可以多次赋值基本数据类型:否引用数据类型:是
声明前是否可以访问是,值为undefined

模板字符串(标签函数)

console.log`hello world` // ["hello world", raw: Array(1)]

const name = 'jcode'
const gender = true

function myTagFn (strings, name, gender1) {
  // 被插值表达式分割的数组
  // ["hello, ", " i am ", "", raw: Array(3)]"jcode" true
	// console.log(strings, name, gender1)
  // return 123
  const sex = gender1 ? 'man' : 'woman'
  return strings[0] + name + strings[1] + sex + strings[2]
}

const result = myTagFn`hello, ${name} i am ${gender}.` 
console.log(result)

字符串扩展方法

const msg = 'abcd'
console.log(
    // msg.startsWith('a')
    // msg.endsWith('cd')
    msg.includes('c')
)

Object.assign

const source1 = {
    a: 1,
    b: 2
}

const source2 = {
    e: 4,
    f: 5
}

const target = {
    a: 11,
    c: 3
}

const result = Object.assign(target, source1, source2)
console.log(result) // { a: 1, c: 3, b: 2, e: 4, f: 5 }

console.log(result === target) // true

Object.is

console.log(
    // 0 == false // true
    // 0 === false // false
    // +0 === -0 // true
    // Nan === Nan // false
    // Object.is(+0, -0) // false
    Object.is(NaN, NaN) // true
)

Number.isInteger()方法用来判断给定的参数是否为整数。

function fits(x, y) {
  if (Number.isInteger(y / x)) {
    return 'Fits!';
  }
  return 'Does NOT fit!';
}

console.log(fits(5, 10));
// expected output: "Fits!"

console.log(fits(5, 11));
// expected output: "Does NOT fit!"

Proxy

const user = {
  name: 'ace',
  age: 19,
  brother: {
    name: '路飞',
    age: 17
  }
}

const proxyUser = new Proxy(user, {
  // 获取目标对象的某个属性值
  get (target, p, receiver) {
    console.log('get方法调用了')
    return Reflect.get(target, p)
  },
  // 修改目标对象的属性值/为目标对象添加新的属性
  set (target, p, value, receiver) {
    console.log('set方法调用了')
    return Reflect.set(target, p, value)
  },
  // 删除目标对象上的某个属性
  deleteProperty (target, p) {
    console.log('deleteProperty调用了')
    return Reflect.deleteProperty(target, p)
  }
})
console.log(user)
// 通过代理对象更新目标对象
proxyUser.name = '萨博'
// 通过代理对象获取目标对象中的某个属性值
console.log(proxyUser.name, proxyUser.brother)
console.log(user)
// 通过代理对象向目标对象中添加一个新的属性
proxyUser.gender = '男'
console.log(user)
// 通过代理对象删除目标对象中的属性
delete proxyUser.age
console.log(user)
// 更新目标对象中的某个属性对象中的属性值(深度监听)
proxyUser.brother.name = '白胡子'
console.log(user)


/**
 * 总结:如果操作代理对象,目标对象中的数据也会随之变化,同时如果想要在操作数据的时候,界面也要跟着重新更新渲染,那么也是操作代理对象
 * 通过当前的代理对象找到该对象中的某个属性,更改该属性中的某个数组的数据
 * */

Reflect

const o = {
	name: '路飞',
  age: 19
}
console.log('name' in o)
console.log(delete o['age'])
console.log(Object.keys(o))

// 等同于
console.log(Reflect.has(o, 'name'))
console.log(Reflect.deleteProperty(o, 'age'))
console.log(Reflect.ownKeys(o))

静态方法 - MDN链接

Reflect.apply(target, thisArgument, argumentsList)

  • 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。

Reflect.construct(target, argumentsList[, newTarget])

  • 对构造函数进行 new 操作,相当于执行 new target(...args)。

Reflect.defineProperty(target, propertyKey, attributes)

  • 和 Object.defineProperty() 类似。如果设置成功就会返回 true

Reflect.deleteProperty(target, propertyKey)

  • 作为函数的delete操作符,相当于执行 delete target[name]。

Reflect.get(target, propertyKey[, receiver])

  • 获取对象身上某个属性的值,类似于 target[name]。

Reflect.getOwnPropertyDescriptor(target, propertyKey)

  • 类似于 Object.getOwnPropertyDescriptor()。如果对象中存在该属性,则返回对应的属性描述符, 否则返回 undefined.

Reflect.getPrototypeOf(target)

  • 类似于 Object.getPrototypeOf()。

Reflect.has(target, propertyKey)

  • 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

Reflect.isExtensible(target)

  • 类似于 Object.isExtensible().

Reflect.ownKeys(target)

  • 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响).

Reflect.preventExtensions(target)

  • 类似于 Object.preventExtensions()。返回一个Boolean。

Reflect.set(target, propertyKey, value[, receiver])

  • 将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。

Reflect.setPrototypeOf(target, prototype)

  • 设置对象原型的函数. 返回一个 Boolean, 如果更新成功,则返回true。

类(class)

类的静态方法

  • 定义:类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。
  • 静态方法调用直接在类上进行,不能在类的实例上调用。静态方法通常用于创建实用程序函数。
class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(this)
    console.log(`hello, ${this.name}`)
  }

  static bye (name) {
    console.log(this, name)
  }
}

const p = new Person('ace')

p.say()

Person.bye('static bye')

类的继承

    • 父类的静态属性和方法也能继承
  • super关键字用于访问和调用一个对象的父对象上的函数。
    • 在构造函数中使用时,super关键字将单独出现,并且必须在使用this关键字之前使用。super关键字也可以用来调用父对象上的函数。

Set

  • 定义:Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用
  • 值得相等
    • 因为 Set 中的值总是唯一的,所以需要判断两个值是否相等。在ECMAScript规范的早期版本中,这不是基于和===操作符中使用的算法相同的算法。具体来说,对于 Set s, +0 (+0 严格相等于-0)和-0是不同的值。然而,在 ECMAScript 2015规范中这点已被更改。有关详细信息,请参阅浏览器兼容性 表中的“Key equality for -0 and 0”。

另外,NaN和undefined都可以被存储在Set 中,NaN之间被视为相同的值(NaN被认为是相同

的,尽管 NaN !== NaN)。

const s = new Set()

s.add(1).add(2).add(3).add(2).add('2').add(4)

console.log(s)

s.forEach(item => console.log(item))
console.log('-----------')

for (let val of s) {
  console.log(val)
}
console.log('============')


console.log(s.size)

console.log(s.has(100))

console.log(s.delete(3))

const arr = [9, 8, 7, 6]
// const res = Array.from(new Set(arr))
const res = [...new Set(arr)]
console.log(res)

Map

  • Map对象
    • Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
  • 普通对象的键只能是字符串,不管传入什么类型的值,都会被toString()
const obj = {}
obj[123] = '123'
obj[true] = 'true'
obj[{ a: 1 }] = 'a'

console.log(Object.keys(obj)) // [ '123', 'true', '[object Object]' ]

Symbol

  • symbol 是一种基本数据类型 (primitive data type)。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:"new Symbol()"。
  • 最主要的作用就是为对象添加独一无二的属性名
const obj = {
  [Symbol()]: 'abc',
  [Symbol()]: 123
}

console.log(obj) // { [Symbol()]: 'abc', [Symbol()]: 123 }



const name = Symbol()

const o = {
  [name]: 'o-name',
  name: 'ha'
}

// console.log(name)
// 由于Symbol的唯一性,外部不能访问,这就创建了一个私有变量
console.log(o[name]) // 'o-name'
console.log(o.name) // 'ha'

console.log(
  // Symbol() === Symbol() // false
  Symbol('foo') === Symbol('foo') // false
)

const s1 = Symbol.for('foo')
const s2 = Symbol.for('foo')
console.log(s1 === s2) // true

console.log(
  Symbol.for(true) === Symbol.for('true') // true
)

const ob = {
  [Symbol.toStringTag]: 'hhh'
}
console.log(ob.toString()) // [object hhh]

// for in / Object.keys() / JSON.stringify() 都不能获取到Symbol键值
// Object.getOwnPropertySymbols() 这个属性可以获取到, 与Object.keys()类似
console.log(Object.getOwnPropertySymbols(ob)) // [ Symbol(Symbol.toStringTag) ]

for of

iterator

  • Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被for...of循环使用。
// 实现迭代器接口
const obj = {
    store: ['a', 'b', 'c'],
    [Symbol.iterator]: function () {
        let index = 0
        const self = this
        return {
            next: function () {
                const result = {
                    value: self.store[index],
                    done: index >= self.store.length
                    
                }
                index++
                return result
            }
        }
    }
}

for (const value of obj) {
    console.log(value)
}

// 迭代器模式
const todos = {
    life: ['弛放', '睡觉', '打豆豆'],
    learn: ['语文', '数学', '英语'],
    work: ['喝茶'],
    each: function (callback) {
        const all = [].concat(this.life, this.learn, this.work)
        for (const item of all) {
            callback(item)
        }
    },
    [Symbol.iterator]: function () {
        const all = [...this.life, ...this.learn, ...this.work]
        let index = 0
        return {
            next: function () {
                return {
                    value: all[index],
                    done: index++ >= all.length
                }
            }
        }
    }
}

todos.each(item => {
    console.log(item)
})
console.log('----------')

for (const value of todos) {
    console.log(value)
}

Generator

function * foo () {
  console.log('start')

  const res = yield 'foo'
  console.log(res)

  const error = yield 'e'
  console.log(error)
}

const generator = foo()

const result = generator.next()
console.log(result) // {value: 'foo', done: false}

const result2 = generator.next('2') // 第5行会打印 2,这里传递的参数会作为yield的返回值
console.log(result2) // {value: undefined, done: false)

const error = generator.next(new Error('这是一个错误'))
console.log(error)

/**
 * 调用next()方法会返回 yield 后面的值,next(data)传递的参数,会作为yield的返回值
 * */



// 使用 Generator 应用发号器
function * createIdMaker () {
	let id = 1
  while (true) {
  	yield id++
  }
}

const idMaker = createIdMaker()
console.log(idMaker.next().value)
console.log(idMaker.next().value)
console.log(idMaker.next().value)

// 使用 Generator 函数实现 iterator 方法
const todos = {
    life: ['弛放', '睡觉', '打豆豆'],
    learn: ['语文', '数学', '英语'],
    work: ['喝茶'],
    [Symbol.iterator]: function * () {
        const all = [...this.life, ...this.learn, ...this.work]
        for (const item of all) {
            yield item
        }
    }
}

for (const value of todos) {
    console.log(value)
}

ES 2016

includes 数组的查找

const arr = ['foo', 1, 2, NaN, true]

// includes 数组的查找, 能查找判断NaN
console.log(arr.indexOf('foo')) // 0
console.log(arr.indexOf('bar')) // -1
console.log(arr.indexOf('NaN')) // -1
console.log(arr.includes(true)) // true
console.log(arr.includes(NaN)) // true

指数运算 **

// 指数运算符
console.log(Math.pow(2, 3))
console.log(2 ** 4)

ES 2017

Objece 扩展方法

Object.values() 和 Object.entries()

const obj = {
  name: 'jcode',
  age: 18,
  height: 183
}

console.log(Object.values(obj)) // [ 'jcode', 18, 183 ]

console.log(Object.entries(obj)) // [ [ 'name', 'jcode' ], [ 'age', 18 ], [ 'height', 183 ] ]

// 转换完后用for of 遍历
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value) // name jcode \n age 18 \n height 183
}

// 转换完后,符合Map对象格式
console.log(new Map(Object.entries(obj))) // Map(3) { 'name' => 'jcode', 'age' => 18, 'height' => 183 }

Object.getOwnPropertyDescriptors()

const o1 = {
  firstName: 'j',
  lastName: 'code',
  get fullName () {
    return this.firstName + ' ' + this.lastName
  },
  sayHi () {
    console.log('haha')
  }
}

// 这种方式,只会把fullName定义为普通键值对, sayHi定义为函数
const o2 = Object.assign({}, o1)
o2.firstName = 'jj'
console.log(o2.fullName) // 'j code'
console.log(o2) // { firstName: 'jj', lastName: 'code', fullName: 'j code' }

const descriptors = Object.getOwnPropertyDescriptors(o1)
const o3 = Object.defineProperties({}, descriptors)
o3.firstName = 'jjj'
console.log(o3.fullName)

const obj = {
  name: 'jcode',
  age: 18,
  height: 183
}

console.log(Object.values(obj)) // [ 'jcode', 18, 183 ]

String.prototype.padStart | String.prototype.padEnd

console.log('12'.padStart(5, '0')) // 00012

Async / Await

ES 2020/2021 新特性

  • 空值合并运算符
function foo (option) {
  // 只有 size = null 或者 undefined
  option.size = option.size ?? 100
  
  const mode = option.mode || 'hash' 
  console.log(option)
}

foo({ size: 0 })
  • 可选链运算符
const list = [
  {
    title: 'foo',
    author: {
      name: 'zs',
      email: 'zs@qq.com'
    }
  },
  {
    title: 'bar'
  }
]
list.forEach(item => {
  console.log(item.author?.name)
})

ES11(2020)

1. Nullish coalescing Operator(空值处理)

// 表达式在 ?? 的左侧 运算符求值为undefined或null,返回其右侧。
let user = {
    u1: 0,
    u2: false,
    u3: null,
    u4: undefined
    u5: '',
}
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(可选链)

  • ?. 用户检测不确定的中间节点
let user = {}
let u1 = user.childer.name // TypeError: Cannot read property 'name' of undefined
let u1 = user.childer?.name // undefined

3. Promise.allSettled

  • 返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应 的promise结果
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise4 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1,promise2,promise3, promise4]
Promise.allSettled(promiseList)
.then(values=>{
  console.log(values)
});

4. import('a' + 'xxx.js') - 动态按需导入

5. 新基本数据类型BigInt - 任意精度的整数

6. globalThis

  • 浏览器:window
  • worker:self
  • node:global

ES12(2021)

1. replaceAll

  • 返回一个全新的字符串,所有符合匹配规则的字符都将被替换掉
const str = 'hello world';
str.replaceAll('l', ''); // "heo word"

2. Promise.any

  • Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的
  • promise 。如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise
const promise1 = new Promise((resolve, reject) => reject('我是失败的Promise_1'));
const promise2 = new Promise((resolve, reject) => reject('我是失败的Promise_2'));
const promiseList = [promise1, promise2];
Promise.any(promiseList)
.then(values=>{
  console.log(values);
})
.catch(e=>{
  console.log(e);
});

3. WeakRefs

  • 使用WeakRefs的Class类创建对对象的弱引用(对对象的弱引用是指当该对象应该被GC回收时不会阻止GC的回收行为)

4. 逻辑运算符和赋值表达式

  • 逻辑运算符和赋值表达式,新特性结合了逻辑运算符(&&,||,??)和赋值表达式而JavaScript已存在的复
// 合赋值运算符有:
a ||= b
//等价于
a = a || (a = b)

a &&= b
//等价于
a = a && (a = b)

a ??= b
//等价于
a = a ?? (a = b)

5. 数字分隔符

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

1_000_000_000 === 1000000000; // true

结尾

文中如有错误欢迎读者们在评论区中指出,如果有其他好用的新特性,也欢迎在评论区补充,持续更新中。。。