ECMA Script 新特性

414 阅读7分钟

1. 概述

  • ECMAScript 是 JavaScript 的标准化规范
  • ECMAScript 只提供了基本语法
  • JavaScript 是 ECMAScript 的扩展语言
  • 浏览器环境的 JavaScript = ECMAScript + DOM + BOM
  • node 环境的 JavaScript = ECMAScript + fs + net + etc.
  • ES2015 相比较 ES5.1 的变化
    • 解决原有语法上的一些问题或不足
    • 对原有语法进行增强
    • 全新的对象,全新的方法,全新的功能
    • 全新的数据类型和数据结构

2. 作用域

  • 作用域:某个成员能够起作用的范围
  • ES2015 之前只有两种作用域,分别是全局作用域和函数作用域
  • ES2015 中新增了块级作用域
  • 在块级作用域内定义的成员外部无法访问
  • 全局作用域
    • var 关键字声明的成员在全局作用域,具有变量提升的特性
  • 块级作用域
    • 花括号形成的块的作用域
    • let 和 const 都可以声明块级作用域成员,没有变量提升的特性,使用闭包的原理将变量封锁在一个独立的块内
    • const 声明常量,在 let 的基础上多了一个只读特性,一经声明,必须赋值,声明的成员不能修改
      • 注:不允许修改指的是不允许指向另一个新的内存地址(即引用)

3. 数组的解构

  • 声明解构语法,变量必须对应所对应的值

    const arr = [100, 200, 300]
    const [foo, bar, baz] = arr
    console.log(foo, bar, baz) // 100, 200, 300
    
  • ... 表示把剩余数据存放到变量中(以数组方式返回),只能在最后一个成员使用

    const arr = [100, 200, 300]
    const [foo, ...reset] = arr
    console.log(reset) // [200, 300]
    
  • 声明的变量少于数组的长度,会按照从左到右的顺序赋值

    const arr = [100, 200, 300]
    const [foo] = arr
    console.log(foo) // 100
    
  • 声明的变量大于数组的长度,多出来的变量为 undefined

    const arr = [100, 200, 300]
    const [foo, bar, baz, more] = arr
    console.log(more) // undefined
    
  • 可以给变量设置默认值

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

4. 对象的解构

  • 解构的变量要对应对象中的 key

    const obj = { name: 'kjy', age: 24 }
    const { name } = obj
    console.log(name) // kjy
    
  • 重命名变量

    const obj = { name: 'kjy', age: 24 }
    const name = 'kxy'
    const { name: myName } = obj
    console.log(myName) // kjy
    
  • 如果成员没有匹配到,则变量的值为 undefined

    const obj = { name: 'kjy', age: 24 }
    const { sex } = obj
    console.log(sex) // undefined
    
  • 设置默认值

    const obj = { name: 'kjy', age: 24 }
    const { sex = '男' } = obj
    console.log(sex) // 男
    

5. 模板字符串

  • 支持换行

  • 支持通过插值表达式(${ 变量/JavaScript 语句 })的方式嵌入数据

  • 用法

    const name = 'kjy'
    const str = `My name is ${ name }`
    console.log(str) // My name is kjy
    
  • 带标签的模板字符串

    • 作用:对模板字符串进行加工,标签函数的返回值就是模板子字符串接收的值

      const name = 'kjy'
      const gender = true
      
      function myTagFn(strings, name, gender) {
      	console.log(strings) // ['My name is ', ', sex is ']
      	gender = gender ? '男' : '女'
      	retunr strings[0] + name strings[1] + gender
      }
      
      const result = myTagFn`My name is ${ name },gender is ${ gender }`
      console.log(result) // My name is kjy, gender is 男
      

6. 字符串的扩展方法

  • inclueds() 包含某个字符

  • startsWith() 以某个字符开始

  • endsWith() 以某个字符结束

    const message = 'Error: foo is not defined'
    console.log(message.inclueds('Error')) // true
    console.log(message.startsWith('Error')) // true
    console.log('message.endsWith('defined')') // true
    

7. 参数默认值

  • 当没有传入参数或者 undefined 时使用默认值

    function foo(enable){
    	console.log(enable)
    }
    
    foo(true) // true
    
    function foo(enable = false){
    	console.log(enable)
    }
    
    foo() // false
    
  • 注:带有默认值的参数一定要放到参数列表最后

8. ... 操作符

  • 使用 ... 操作符可以表示剩余操作符,以数组的形式接收传入参数

    function foo(...args){
    	console.log(args) // [1, 2, 3]
    }
    
    foo(1, 2, 3)
    
  • 注:... 操作符只能出现在形参的最后一位,只可以使用一次

  • 使用 ... 操作符可以展开数组

    const arr = ['foo', 'bar', 'baz']
    
    console.log(...arr) // foo bar baz
    

9. 箭头函数

  • 传统的函数

    function foo(number) {
    	return number + 1
    }
    
    console.log(foo(100)) // 101 
    
  • 箭头函数

    const foo = number => number + 1
    
    console.log(foo(100)) // 101
    
    const inc = (n, m) => {
    	return n + 1
    }
    
    console.log(inc(100)) // 101
    
    const arr = [1, 2, 3, 4, 5]
    
    const arr1 = arr.filter(item => item % 2 === 0)
    
    console.log(arr1) // [2, 4]
    
    • 箭头函数不会改变 this 指向,箭头函数中的 this 永远指向当前作用域中的 this

      const person = {
      	name: 'kjy',
      	sayHi: function (){
      		console.log(`hi my name is ${this.name}`) // my name is kjy
      	},
      	sayHi: () => {
      		console.log(`hi my name is ${this.name}`) // my name is undefined
      	}
      }
      

10. 对象字面量的增强

const bar = '345'
const obj = {
	foo: 123,
	bar,
	method1 () {
		console.log('method1') // method1
		console.log(this) // { foo: '123', bar: '345', method1: Function: method1 }
	},
	// 动态属性
	[Math.random()]: 123
}

11. Object 扩展方法

  • Object.assign()

    • Object.assign() 可以将多个源对象中的属性复制到一个目标对象中

    • 如果有相同的属性,源对象的属性会覆盖掉目标对象中的属性

      const source1 = {
      	a: 123,
      	b: 456
      }
      
      const target = {
      	a: 789,
      	c: 101112
      }
      
      const result = Object.assign(target, source1)
      
      console.log(target) // { a: 123, c: 101112, b: 456 }
      console.log(result === target) // true
      
  • Object.is()

    • Object.is() 可以判断两个值是否相等

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

12. Proxy 代理对象

  • 很容易监听到属性的变更

  • Proxy 是以非侵入的方式监管了整个对象的读写

    const person = {
    	name: 'kjy',
    	age: 24
    }
    
    const personProxy = new Proxy(person, {
    	// 监视获取属性
    	// target 代理目标对象
    	// 操作属性
    	get (target, property) {
    		console.log(target, property) // { name: 'kjy', age: 24 } name
    		return target[property]
    	},
    	// 监视设置属性
    	// target 代理目标对象
    	// property 操作属性
    	// value 设置的值
    	set (target, property, value) {
    		target[property] = value
    	},
    	// 删除目标对象属性时自动执行
    	deleteProperty (target, property) {
    		delete target[property]
    	}
    })
    
    console.log(personProxy.name) // kjy
    

13. reflect

  • 统一的对象操作 API

  • Reflect 属于一个静态类,不能通过 new 的方式构建一个实例对象

  • Reflect 内部封装了一系列对对象的底层操作

  • Reflect 成员方法就是 Proxy 处理对象的默认实现

    const obj = {
    	foo: '123',
    	bar: '456'
    }
    
    const objProxy = new Proxy(obj, {
    	get (target, property) {
    		return Reflect.get(target, property)
    	}
    })
    console.log(objProxy.foo) // 123
    

14. Promise

  • 解决了传统异步编程中回调函数嵌套过深的问题

15. class 类

  • 之前定义类型的方式

    • 通过构造函数的方式来定义类型

      function Person(name){
      	this.name = name
      }
      Person.prototype.say = function () {
      	console.log(`hi,my name is ${this.name}`)
      }
      
  • ES2015 之后定义类型的方式

    • 通过 class 关键字定义类型

      class Person{
      	constructor(name){
      		this.name = name
      	}
      	// 实例方法
      	say (){
      		console.log(`hi,my name is ${this.name}`)
      	}
      	// 静态方法
      	static create(name){
      		return new Person(name)
      	}
      }
      
      // 类的继承
      class Student extends Person {
      	// Student 中 拥有 Person 的所有成员
      	constructor(name, number){
      		// 调用 super 相当于调用了父类的构造函数
      		super()
      		this.number = number
      	}
      }
      
      const p = new Person('kjy')
      p.say() // hi,my name is kjy
      
      

16. Set 数据结构

  • Set 中的成员不允许重复

    const s = new Set()
    
    // add 方法返回实例对象本身,所以可以链式调用
    // 如果添加重复值,重复的值会被忽略掉
    s.add(1).add(2).add(3).add(1)
    
    console.log(s) // Set {1, 2, 3}
    
    // 获取 Set 实例中的长度
    s.size // 3
    // 判断集合中是否包含指定的值
    s.has(100) // false
    // 删除集合中指定的值
    s.delete(3) // true
    // 清除实例对象
    s.clear()
    
    // 利用 Set 给数组去重
    const arr = [1, 2, 3, 4, 1, 2]
    // Set 构造函数接收一个数组
    const result = new Set(arr)
    console.log(result) Set {1, 2, 3, 4}
    

17. Map

  • 键值对集合,用来映射两个任意类型之间的对应关系

    const m = new Map()
    
    const tom = { name: 'tom' }
    
    // 通过 set 方法设置键值对
    m.set(tom, 90)
    console.log(m) // Map { { name: 'tome' => 90 } }
    // 通过 get 方法获取某一个键的值
    m.get(tom) // 90
    // 通过 has 方法判断某个键是否存在
    m.has(tom) // true
    // 通过 delete 方法删除某个键
    m.delete(tom) // true
    // 通过 clear 方法情况 Map 集合
    m.clear() // true
    

18. Symbol

  • 表示一个独一无二的值
  • Symbol.for() 接收一个字符串作为参数,相同的参数,Symbol 值也是相同的

19. for of 循环

  • 之后会作为所有数据结构的统一方式

    const arr = [100, 200, 300, 400, 500]
    
    // for of 循环拿到的是数组中元素,而不是对应的下标
    for (const item of arr){
    	console.log(item) // 100 200 300 400 500
    }
    

20. 可迭代接口 Iterable

  • 实现了 Iterable 接口就是 for of 的前提

  • 对外提供统一遍历接口

    const s = new Set(['foo', 'bar', 'baz'])
    const iterator = s[Symbol.iterator]()
    console.log(iterator) // { value: 'foo', done: false }
    
  • 实现可迭代接口

    const obj = {
    	name: 'kjy',
        age: 24,
        store: ['foo', 'bar', 'baz'],
        // 可迭代接口 iterable
        [Symnol.iterator]: function(){
        	let index = 0
        	const self = this
        	// 迭代器对象 iterator
        	return {
        		next: function (){
        			// 迭代结果接口 iterationResult
        			const result =  {
        				value: self.store[index],
        				done: index++ >= store.length
        			}
        			return result
        		}
        	}
        }
    }
    

21. 生成器函数 Generator

  • 避免异步编程中回调嵌套过深,提供更好的异步编程解决方案

  • 生成器函数返回一个生成器对象,生成器对象中存在用于迭代的 next 方法

    function * foo(){
    	console.log('kjy')
    	return 100
    }
    const result = foo()
    console.log(result) // Object [Generator] {}
    console.log(result.next()) // { value: 100, done: true }
    
  • yield 的使用

    • yield 关键词用于暂停函数的执行,yield 后边的值作为 next 的结果返回
    function * foo(){
    	console.log('11111')
    	yield 100
    	console.log('22222')
    	yield 200
    	console.log('33333')
    	yield 300
    }
    
    const result = foo()
    console.log(result.next()) // 11111 { value: 100, done: false }
    console.log(result.next()) // 22222 { value: 200, done: false }
    console.log(result.next()) // 33333 { value: 300, done: false }