ECMAScript新特性

146 阅读5分钟

JavaScript的语言本身

通常看作 JavaScript 的标准化规范

实际上 JavaScript 是 ECMAScript 的拓展语言

ECMAScript 只是提供了做基本的语法


let 与 块级作用域

作用域: 某个成员能够起作用的范围

ES2015 之前 JavaScript 的作用域有:

  • 全局作用域
  • 函数作用域

ES2015 新增了:

  • 块级作用域

let const

const 在 let 的基础上增加了 只读 的功能

最佳实践: 不用 var 主用 const 需要时用 let

数组的解构

const arr = [100, 200, 300]

const [foo, bar, baz] = arr
console.log(foo, bar, baz) //100 200 300

const [, , t] = arr
console.log(t) //300

const [p, ...rest] = arr
console.log(rest) //[ 200, 300 ]

const [o, tw, th, f] = arr
console.log(f) //undefined

const [y, e, s = 321, si = 'default'] = arr
console.log(s, si) //300 default

对象的解构

const obj = { name: 'ds', age: 18 }

const { name } = obj
console.log(name) // ds

const age = 19
const { age: age1 } = obj
console.log(age1)

const { name: objName = 'jack' } = obj
console.log(objName)

模板字符串

  • 直接换行
  • 插值表达式
  • 带标签的模板字符串
const name = 'tom'
const gender = true

function myTagFunc (strings, name, gender) {
  return strings[0] + name + strings[1] + gender
}

const result = myTagFunc`hey, ${name} is a ${gender}`
console.log(result)

字符串的拓展方法

  • includes()
  • startsWith()
  • endsWith()
const message = 'Error: foo is not defined.'

console.log(
  message.startsWith('Error'),
  message.endsWith('.'),
  message.includes('foo')
)

参数默认值

function foo (bar, enable = true) {
    console.log(enable)
}
foo()

剩余参数

function foo (...args) {
  console.log(args)
}
foo(1, 2, 3, 4)

展开数组

const arr = ['foo', 'bar', 'baz']

console.log.apply(console, arr)
console.log(...arr)

箭头函数

const inc = n => n + 1
console.log(inc(100))

const arr = [1, 2, 3, 4]
arr.filter(function(item) {
    return item % 2
})
arr.filter(i => i % 2)

箭头函数不会改变 this 指向

const person = {
  name: 'tom',

  sayHello: function () {
    console.log(this.name)
  },

  sayHi: () => {
    console.log(this.name)
  },

  sayHiAsync: function () {
    setTimeout(function () {
      console.log(this.name)
    }, 1000)
  },

  sayHelloAsync: function () {
    setTimeout(() => {
      console.log(this.name)
    }, 1000)
  }
}

person.sayHello() // tom

person.sayHi() // undefined

person.sayHiAsync() // undefined

person.sayHelloAsync() // tom

对象字面量的增强

const bar = '233'

const obj = {
  foo: 123,
  // bar: bar,
  bar,
  // method: function () {}
  method () {
    console.log(this)
  },
  [Math.random()]: 889
}

obj.method()

console.log(obj)

对象拓展方法

  • Object.assign
// 将多个源对象中的属性复制到一个目标对象中
const source = {
  a: 123,
  b: 123,
  c: {
    k: 'dsa'
  }
}
const target = {
  a: 456,
  c: {
    l: 'dawsd'
  }
}

const result = Object.assign(target, source)

console.log(target) // { a: 123, c: { k: 'dsa' }, b: 123 }
console.log(target === result) // true

function func (obj) {
  const funcObj = Object.assign({}, obj)
  funcObj.name = 'func obj'
  return funcObj
}

const obj = { name: 'global obj' }

console.log(func(obj)) // { name: 'func obj' }

console.log(obj) // { name: 'global obj' }
  • Object.is
console.log(0 == false) // true
console.log(0 === false) // false
console.log(+0 === -0) // true
console.log(NaN === NaN) // false

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

Proxy

const person = {
  name: 'dasd',
  age: 23
}

const personProxy = new Proxy(person, {
  get (target, property) {
    return property in target ? target[property] : 'default'
  },
  set (target, property, value) {
    if (property === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError(`${value} is not an int`)
      }
    }
    target[property] = value
    // console.log(target, property, value)
  }
})

personProxy.age = 100
personProxy.gender = true
personProxy.name = 'biki'

console.log(personProxy.name)
console.log(personProxy.xxx)
console.log(personProxy)

Proxy vs Object.defineProperty()

  • defineProperty 只能监视属性的读写
  • Proxy 能够监视到更多对象操作

const person = {
  name: 'dsad',
  age: 23
}

const personProxy = new Proxy(person, {
  deleteProperty (target, property) {
    console.log('delete', property)
    delete target[property]
  }
})

delete personProxy.age
console.log(person)
  • Proxy 更好的支持数组对象的监视
const list = []

const listProxy = new Proxy(list, {
  set (target, property, value) {
    console.log('set', property, value)
    target[property] = value
    return true
  }
})

listProxy.push(100)

//set 0 100
// set length 1
  • Proxy 是以非侵入的方式监管了对象的读写

Reflect

  • Reflect 属于一个静态类
  • Reflect 内部封装了一系列对对象的底层操作
  • Reflect 成员方法就是 Proxy 处理对象的默认实现
const obj = {
  foo: '123',
  bar: '456'
}

const proxy = new Proxy(obj, {
  get (target, property) {
    console.log('watch logic~')

    return Reflect.get(target, property)
  }
})

console.log(proxy.foo)
  • Reflect 统一提供一套用于操作对象的 API
const obj = {
  name: 'das',
  age: 18
}
// console.log('name' in obj)
// console.log(delete obj['age'])
// console.log(Object.keys(obj))

console.log(Reflect.has(obj, 'name'))
console.log(Reflect.deleteProperty(obj, 'age'))
console.log(Reflect.ownKeys(obj))

Promise

  • 一种更优的异步编程解决方案
  • 解决了传统异步编程中回调函数嵌套过深的问题

class 类

// function Person (name) {
//   this.name = name
// }

// Person.prototype.say = function () {
//   console.log(`hi, my name is ${this.name}`)
// }

class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`hi, my name is ${this.name}`)
  } 
}

const p = new Person('biki')
p.say()

实例方法 vs. 静态方法

class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`hi, my name is ${this.name}`)
  } 

  static create (name) {
    console.log(this) // [class Person]
    return new Person(name)
  } 
}

const p = Person.create('biki')
p.say()

类的继承

class Person {
  constructor (name) {
    this.name = name
  }

  say () {
    console.log(`hi, my name is ${this.name}`)
  } 

  static create (name) {
    console.log(this) // [class Person]
    return new Person(name)
  } 
}

class Student extends Person {
  constructor (name, number) {
    super(name)
    this.number = number
  }
  hello () {
    super.say()
    console.log(`my school number is ${this.number}`)
  }
}

const s = new Student('jack', 100)

s.hello()

Set 数据结构

const s = new Set()

s.add(1).add(2).add(3)
console.log(s)
console.log(s.size)
console.log(s.has(100))
console.log(s.delete(3))
console.log(s)
s.clear()
console.log(s)

const arr = [1, 3, 3, 2, 1, 4]
const result = Array.from(new Set(arr)) || [...new Set(arr)]
console.log(result)

Map 数据结构

const obj = {}
obj[true] = 'value'
obj[123] = 'value'
obj[{ a: 1 }] = 'value'

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

const m = new Map()

const tom = { name: 'tom' }

m.set(tom, 90)

console.log(m) // Map(1) { { name: 'tom' } => 90 }

console.log(m.get(tom)) // 90

console.log(m.has(tom)) // true

m.delete(tom)
console.log(m) // Map(0) {}

m.set('dsa', true)
console.log(m.get('dsa')) // true
m.clear()
console.log(m) // Map(0) {}

m.set('biki', 100)

m.forEach((value, key) => {
  console.log(value, key)
})

Symbol

const s = Symbol()
console.log(s) // Symbol()
console.log(typeof s) // symbol

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


// 实现私有成员

// a,js =======================================

const name = Symbol()
const person = {
  [name]: 'dsad',
  say () {
    console.log(this[name])
  }
}

// b.js =======================================

person[Symbol()] // 访问不到
person.say()

Symbol 最主要的作用就是为对象添加独一无二的属性名

其他

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

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

console.log(Symbol.iterator) // Symbol(Symbol.iterator)
console.log(Symbol.hasInstance) // Symbol(Symbol.hasInstance)

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

====================================================================

const  obj = {
  [Symbol('dsad')]: 'symbol value',
  foo: 'normal value'
}

for (var key in obj) {
  console.log(key) // 拿不到 Symbol 值
}
console.log(Object.keys(obj)) // [ 'foo' ]
console.log(JSON.stringify(obj)) // {"foo":"normal value"}

console.log(Object.getOwnPropertySymbols(obj)) //[ Symbol(dsad) ]

for...of

for...of 将作为遍历所有数据结构的统一方式

const arr = [100, 200, 300, 400]

for (const item of arr) {
  console.log(item)
}

// 虽然类似于 for...Each
arr.forEach(item => {
  console.log(item)
})

// 但是 for...of 可以使用 break 随时终止循环 forEach 不能
for (const item of arr) {
  console.log(item)
  if (item > 100) {
    break
  }
}

// 其他可以跳出循环的遍历方法
arr.some() // 返回 true
arr.every() // 返回 false

// 遍历 Set
const s = new Set(['foo', 'bar'])

for (const item of s) {
  console.log(item)
}

// 遍历 Map
const m = new Map()
m.set('foo', '123')
m.set('bar', '321')

for (const item of m) {
  console.log(item)
}

//[ 'foo', '123' ]
//[ 'bar', '321' ]

// 遍历普通对象
const obj = { foo: 124, bar: 433 }

for (const item of obj) { //TypeError: obj is not iterable
  console.log(item)
}

可迭代接口 (iterable)

实现 iterable 接口是 for...of的前提

const set = new Set(['foo', 'bar', 'baz'])

const iterator = set[Symbol.iterator]()

console.log(iterator.next()) // { value: 'foo', done: false }
console.log(iterator.next()) // { value: 'bar', done: false }
console.log(iterator.next()) // { value: 'baz', done: false }
console.log(iterator.next()) // { value: 'baz', done: false }

实现可迭代接口

const obj = {
  store: ['foo', 'bar', 'baz'],

  [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 item of obj) {
  console.log(item)
}

迭代器模式


// 场景: 你我协同开发一个任务清单应用

// 我的代码 ======================================

const todos = {
  life: ['fa', 'dad', 'fafaf'],
  learn: ['dsaddsa', 'dasdsadasdds', 'fggggg'],
  work: ['ewewe'],
  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)
})

// ======================================

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

普通对象使用 for...of

const obj = {
  a: '1',
  b: 2,
  c: { d: 3 },
  [Symbol.iterator]: function () {
    let index = 0
    let keys = Object.keys(this)
    let self = this

    return {
      next: function () {
        return {
          value: self[keys[index]],
          done: index++ >= keys.length
        }
      }
    }
  }
}

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

生成器

function * foo () {
  console.log('1111')
  const a =  yield 100
  console.log(a) // 2000
  console.log('222222')
  const b = yield 200
  console.log(b) // 3000
  console.log('333333')
  const c = yield 300
  console.log(c) // 4000
}

const generator = foo()

console.log(generator.next()) // { value: 100, done: false }
console.log(generator.next(2000)) // { value: 200, done: false }
console.log(generator.next(3000)) // { value: 300, done: false }
console.log(generator.next(4000)) // { value: undefined, done: true }

生成器的应用

// 发号器

function * createIdMaker () {
  let id = 1
  while (true) {
    yield id++
  }
}

const idMaker = createIdMaker()

console.log(idMaker.next())
console.log(idMaker.next())
console.log(idMaker.next())
console.log(idMaker.next())
console.log(idMaker.next())

// iterator

const todos = {
  life: ['fa', 'dad', 'fafaf'],
  learn: ['dsaddsa', 'dasdsadasdds', 'fggggg'],
  work: ['ewewe'],

  [Symbol.iterator]: function * () {
    const all = [...this.life, ...this.learn, ...this.work]
    for (const item of all) {
      yield item
    }
  }
}

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

ES Modules

ES2016 概述

  • Array.prototype.includes
const arr = ['foo', 1, NaN, false]
// 旧
console.log(arr.indexOf('foo'))
// 新
console.log(arr.includes('foo'))
  • 指数运算符
// 旧
console.log(Math.pow(2, 10))
// 新
console.log(2 ** 10)

ES2017 概述

  • Object.values
const obj = {
    foo: 'dsad'
}
console.log(Object.values(obj))
  • Object.entries
const obj = {
  foo: 'eqwe',
  bar: 'dasd'
}

console.log(Object.entries(obj)) // [ [ 'foo', 'eqwe' ], [ 'bar', 'dasd' ] ]

for (const [key, value] of Object.entries(obj)) {
  console.log(key, value)
}
// foo eqwe
// bar dasd

// 普通对象 转成 Map
console.log(new Map(Object.entries(obj))) // Map(2) { 'foo' => 'eqwe', 'bar' => 'dasd' }
  • Object.getOwnPropertyDescriptors
const p1 = {
  firstName: 'li',
  lastName: 'lei',
  get fullName () {
    return this.firstName + ' ' + this.lastName
  }
}

console.log(p1.fullName) // li lei

const p2 = Object.assign({}, p1)
p2.firstName = 'biki'
console.log(p2.fullName) // li lei

const descriptors = Object.getOwnPropertyDescriptors(p1)
console.log(descriptors)

const p3 = Object.defineProperties({}, descriptors)
p3.firstName = 'biki'
console.log(p3.fullName) // biki lei
  • String.prototype.padStart / String.prototype.padEnd
const books = {
  html: 5,
  css: 16,
  javascript: 121
}

for (const [name, count] of Object.entries(books)) {
  console.log(`${name.padEnd(16, '-')}|${count.toString().padStart(3, '0')}`)
}
/**
html------------|005
css-------------|016
javascript------|121
**/
  • 在函数参数中添加尾逗号
// 对代码管理工具 更友好
function foo (
  bar,
  baz,
) {

}

const arr = [
  100,
  200,
  300,
  400,
]
  • Async / Await