Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ECMAScript新特性 #4

Open
janeLLLL opened this issue Aug 31, 2020 · 0 comments
Open

ECMAScript新特性 #4

janeLLLL opened this issue Aug 31, 2020 · 0 comments

Comments

@janeLLLL
Copy link
Owner

[toc]

ECMAScript

缩写ES

  • JavaScript是ES的扩展语言

  • ECMAScript只提供了最基本的语法,只停留在语言层面

  • JavaScript语言本身指的就是ECMAScript

ES2015

概述

ES6

asnyc2017标准

准备

yarn init
code 001.js
yarn add nodemon --dev
yarn nodemon 001.js 
//监视脚本文件

let与块级作用域

  • 全局作用域

  • 函数作用域

  • 块级作用域 {}

    • let 在所声明代码块外部无法访问

    • 循环计数器 嵌套循环 是两层嵌套的独立作用域

      var i重复 内部拿到的只是外部的i

      for(var i ...)
      	for(let i ...)
      		console.log(i)

      var 全局作用域 闭包也可以摆脱影响

      for(let i = 0;i < 3;i ++){
      	let i = 'foo'
      	console.log(i)
      	//互不影响
      }
      /===
      let i = 0
      i++
      if(i < 3){
          let i = 'foo'
          console.log(i)
      }
      • 原有变量声明的提升,不报错,undefined

        let被改正

      console.log(foo)
      var foo = 'zce'

const

恒量常量 只读

只要没有修改指向的内存地址,都是被允许的

特性与let相同

const u = {}
u.hi = '1'//√
u = {}//×

主用const,配合let,不用var

数组的解构

const arr = [1,2,3]
const [foo, bar, baz] = arr

const [foo=3, ...rest] = arr
//提取当前位置往后的所有成员,只能在解构位置最后一个成员上使用

对象的解构

const obj = {name:'zce', age: 18}
const name = 'tom'
const {name: objName='jack'} = obj
console.log(objName)//zce

可以简化代码的编写,体积减小

const { log } = console
log('foo')
log('1')

模板字符串字面量

const str = \`nihao\`nihaonihao`
const mag = `hey, ${name}`

带标签的模板字符串

fuction myTagFunc (string, name, gender){
    console.log(string, name, gender)
    return '123'
}//按照表达式分割的静态的内容,结果是一个数组
const str = myTagFunc`hey,${name} is a ${gender}`
console.log(str)//123
//tag是函数,作为一个模板标签
const arr = console.log`world`

字符串的扩展方法

  • includes()中间包含xx
  • startsWith()判断开头是xx
  • endsWith()判断结尾是xx

参数默认值

没有在传递参数或者传递undefined时使用的一个值

function foo(enable){
    enable = enable === undefined ? true : enable
}
//==
function foo(a, enable = true){
    //一定要出现在参数列表的最后
}
foo()

剩余参数

新增了...

取代argement

出现在最后一位,只能出现一次

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

展开数组

arr = [1,2,3,4]
console.log(...arr)

箭头函数

const omc = n => n + 1
//Fira Code
const arr = [1,2,3,4,5,7]
arr.filter(i => i % 2)

箭头函数与this

  • 没有this的机制,不会改变this的指向,它始终指向的都是当前作用域里的this

  • this在外面是什么,在里面就是什么

  • 在普通函数中,this始终会指向调用它的对象,箭头函数不然

    const person = {
        name : 'tom',
        sayHi : () => {
            console.log(`hi,${this.name}`)
            //在外面this是undefined
        },
        sayHiAsync:function(){
            const _this = this
            //使用了闭包
            setTimeout(function(){
                console.log(_this.name)
            },1000)
        },
        sayHiAsync1:function(){
            setTimeout(() => {
                console.log(this.name)
                //this就是sayHiAsync1作用域中的this
            },1000)
        }
    }
    person.sayHi()
    person.sayHiAsync1()

对象字面量的增强

计算属性名

const obj = {
	foo:2,
	bar,
	[Math.random()]:123
}

Object.assign

  • 多个源对象的属性复制到一个目标对象中

    const a = {}
    const b = {}
    const result = Object.assign(a,b)
    //a === b
    const result = Object.assign(source3,source1,source2)
    //复制的是source2
  • 也可以解决两个对象直接相等改变前者指向地址的问题

    const funcObj = Obj.assign({},obj)

Object.is(对象扩展方法)

==自动转换数据类型

===严格比较

Object.is(+0,-0)
Object.is(NaN,NaN)

Proxy代理对象

Object.defineProperty捕获到对象读写的过程。Vue3.0前实现数据响应,完成双向数据绑定

const person = {
    name: 'zse',
    age: 20
}

const p = new Proxy(person, {
    get(target, property) {
        //目标对象,访问的属性名
        return property in target ? target[property] : 'default'
        //访问属性值,先判断对象中是否存在该属性名
    },//监视属性访问
    set(target, property, value) {
        //代理目标对象,要写入的属性名称,写入的属性值
        console.log(target, property, value)
        //加入数据校验
        if(property === 'age'){
            if(!Number.isInteger(val)){
                throw new TypeError(`1111`)
            }
        }
        target[property] = value

    }//监视属性设置
})

p.gender = true
console.log(p.name)

Proxy VS. defineProperty

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

    //Proxy对象
    deleteProperty(target,property){
    	delete target[property]
    }//监听删除操作
    handler方法 触发方式
    get 读取
    set 写入
    has in操作符
    deleteProperty delete操作符
    getPrototypeOf Object.getPrototypeOf()
    setPrototypeOf Object.setPrototypeOf()
    apply 调用一个函数
    construct 用new调用一个函数
  • Proxy更好的支持数组对象的监视

    以往重写数组的操作方法

    const list = []
    
    const listProxy = new Proxy(list,{
        set(target, property, value){
            console.log(target, property, value)
            //目标对象,数组长度,传值
            target[property] = value
            return true
        }
    })
    
    listProxy.push(100)
  • Proxy是以非侵入的方式监管了对象的读写

Reflect

  • 内部封装了一系列对对象的底层操作(13个)

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

    如果在内部没有定义set get等方法,就默认调用了Reflect同名方法

    ...
    get(target, property){
    	return Reflect.get(target, property)
    }
  • 意义:提供了统一一套用于操作对象的API

    const obj = {
        name : 'hi',
        age:1
    }
    
    // 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))
    
  • new Reflect()本身不可以对数据进行拦截

Promise

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

class 类

  • 以前定义函数和定义函数的原型对象prototype去实现的类型
function Person (name) {
    this.name = name
}//构造函数

Person.prototype.say = function () {
    //对象方法
}
  • 通过class

    class Person {
        constructor (name) {
            this.name = name
        }
        say() {
            console.log(`hi`)
        }
    }
    
    const p = new Person('tom')
    p.say()

静态方法(static)

  • 实例方法 vs. 静态方法

  • 新增了添加静态成员的static关键词

    需要注意this

    class Person {
        constructor (name) {
            this.name = name
        }
        say() {
            console.log(this)
            console.log(`hi`)
        }
        static create (name) {
            console.log(this)
            //this = Person { name: 'tom' }
            return new Person(name)
        }
        //静态方法挂载到类型上,所以静态方法内部的this就不会指向某,而是当前的类型
        //this = [class Person]
    }
    const tom = Person.create('tom')
    tom.say()

类的继承(extends)

以前用原型

class Student extends Person {
    constructor(name ,number){
        super(name)//调用了弗雷的构造函数
        this.number = number
    }
    hello(){
        super.say()
    }
}
  • 子类构造器中super关键字前面不能出现this关键字

Set(集合)

  • 内部成员不允许重复,重复添加会忽略

    const s = new Set()
    s.add(1).add(2).add(3).add(4)
    //返回集合本身,链式调用

    add forEach size(length) have delete clear

  • 作用:为数组元素去重

const arr = [1,2,3,4,2,3,1]
//const result = new Set(arr)是一个对象
const result = Array.from(new Set(arr))
const result = [...new Set(arr)]
//都可以得到一个数组

Map

和对象类似,本质上是键值对集合,但键只能是字符串类型,

const obj = {}
obj[123] = '?'
obj[{a:1}] = '!'

Map可以解决这样的问题

const m = new Map()
const tom = {name : 'tom'}
m.set(tom, 90)
console.log(m.get(tom))
//键值是什么都可以

has delete clear forEach

symbol

  • 一种全新的原始数据类型

  • 避免对象属性名重复产生的问题

    Symbol() != Symbol()
    Symbol('foo')
    Symbol('baz')//描述文本
    ...
    
    const obj = {}
    obj[Symbol()] = '123'
    obj[Symbol()] = '456'
    //因为Symbol的值都是独一无二的,不用担心会冲突
  • 可以模拟实现对象的私有成员

    • 原理:外界无法获取到相同的smybol键值,则无法访问
    //a.js
    const name = Symbol()
    const person = {
        [name]:'1',
        say(){
            console.log(this[name])
        }
    }
    
    //b.js
    person.say()
    //只能通过调用对象方法访问
  • 为对象添加独一无二的属性名

  • 特性:

    • 唯一性 for

      Symbol('foo') != Symbol('foo')
      //与描述文本无关
      
      //for接收一个字符串作为一个参数,相同的字符串一定会返回相同的字符串的值
      const s1 = Symbol.for('foo')
      const s2 = Symbol.for('foo')
      s1 === s2

      它维护的是字符串和smybol之间的关系,如果传参不是字符串,会自动转换为字符串

    • 提供了很多内置的Symbol常量,作为内部方法的标识。可以去实现js中内置的接口。

      const obj = {
          [Symbol.toStringTag]: 'XObject'
          //toStringTag内置的Symbol常量
      }
      console.log(obj.toString())
      //[object object] 对象的toString标签

      但还是可以使用Object.getOwnPropertySymbol(obj)获取到obj中Symbol类型的属性名

for...of循环

  • 遍历所有统一方式

    for (const item of arr){
    	if(...){
            break
        }
    }
    //forEach都不会中止遍历

    伪数组也可

  • 但普通对象Object无法被迭代

    ES2015提供了Iterable接口,表示可迭代的。能实现Iterable接口就是for...of的前提(在内部已经实现了这个接口)

    找原型对象,有Iterable的方法,必须挂载的

    总结:需要返回带有next()方法的对象,不断调用next()方法实现对内部对象的遍历

    const set = new Set(['foo','bar','baz'])
    
    const iterator = set[Symbol.iterator]()//调用set的iterator方法,获得set的迭代器
    
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())

实现可迭代接口(Iterable)

对象内部实现迭代器

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
            }//向后迭代的逻辑,实现了迭代结果的接口,IterationResult
        }//实现了迭代器接口 Iterator
    }
    //挂载一个iterator方法,在这个方法里返回一个迭代器对象
}

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

迭代器模式

  • 意义:对外提供遍历统一接口,外部不用再关心内部的结构。语言层面实现,适用于任何结构。

    const todos = {
        a: [1, 2, 3],
        b: [4, 5, 6],
        c: [7, 8, 9],
    
        [Symbol.iterator]: function () {
            const all = [...this.a, ...this.b, ...this.c]
            let index = 0
            return {
                next: function () {
                    return {
                        value: all[index],
                        done: index++ >= all.length
                        //防止死循环
                    }
                }//返回此次迭代的结果
            }
        }
    }
    
    for (const item of todos) {
        console.log(item)
    }

生成器(Generator)

  • 避免异步编程中回调嵌套过深问题

  • 提供更好的解决方案

  • 内部也有一个迭代器next方法

    function * foo {
    	...
        return 100
    }
    const result = foo()
    console.log(result)
    //打印出来的结构是一个Generator对象
    console.log(result.next())
    //直接调用next方法便可以遍历foo对象,foo才开始进行执行
  • 配合yield使用

  • 总结:

    • 生成器函数会自动返回一个生成器对象,调用它的next方法才会让这个函数体开始执行
    • 一旦中途遇到yield关键字,便会暂停执行,继续next便会继续执行

生成器应用

  1. 发号器
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)
console.log(idMaker.next().value)
console.log(idMaker.next().value)
//简单的发号器需求
  1. 使用Generator函数实现iterator方法

    不用手动实现一个迭代器对象,直接使用循环yield去返回一个生成器的对象

    const todos = {
        a: [1, 2, 3],
        b: [4, 5, 6],
        c: [7, 8, 9],
    
        [Symbol.iterator]: function () {
            const all = [...this.a, ...this.b, ...this.c]
    //        let index = 0
    //        return {
    //            next: function () {
    //                return {
    //                    value: all[index],
    //                    done: index++ >= all.length
    //                    //防止死循环
    //                }
    //            }//返回此次迭代的结果
    //        }
            for(const item of all){
                yield item
            }
        }
    }
    
    for (const item of todos) {
        console.log(item)
    }

ES Modules

  • 语言层面地模块化规范

ES2016

  • 检查数组是否包含指定元素,能去查找NaN

    arr.includes()

  • 指数运算符

    Math.pow(2 ** 10)

ES2017

  • Object.value()

    返回对象中所有的值组成的数组

  • Object.entries()

    返回对象中所有的键值对

    for (const [key, value] of Object.entries(obj)) {
    	console.log(key, value)
    }
  • Object.getOwnProperyDescriptors

    完整地获取对象属性中的描述信息,配合ES中的get和set使用

  • String.prototype / String.prototype.padEnd

    字符串填充方法。用给定的字符串去填充字符串的开始和结束位置,达到指定长度为止

  • 在函数参数中添加逗号

  • Async / Await

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant