美文网首页
笔记三:ECMAScript新特性

笔记三:ECMAScript新特性

作者: 油菜又矮吹 | 来源:发表于2020-07-28 22:10 被阅读0次

    文章内容输出来源:拉勾教育大前端高薪训练营 和自我总结

    ECMAScript 2015

    1.ES2015共有三种作用域

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

    • 全局作用域
    • 函数作用域
    • 块级作用域(ES2015新增)

    2.变量声明:let、const

    • let和const都是块级作用域,let是定义声明变量的,而const是定义声明常量的
    • for点击事件
    var element = [{}, {}, {}]
    for(var i = 0; i < element.length; i++) {
      element[i].onclick = function () {
        // i是全局变量,已经变成了3
        console.log(i)
      }
    }
    element[1].onclick() // 3
    
    var element = [{}, {}, {}]
    for(var i = 0; i < element.length; i++) {
      element[i].onclick = (function (i) {
      // 闭包实现
        return function () {
          console.log(i)
        }
      })(i)
    }
    element[1].onclick() // 2
    
    var element = [{}, {}, {}]
    for(let i = 0; i < element.length; i++) {
      // let定义的变量是块级作用域
      element[i].onclick = function () {
        console.log(i)
      }
    }
    element[1].onclick() // 2
    
    • for生成两层块级作用域
    for(let i = 0; i < 3; i ++) {
      let i = 'foo'
      console.log(i)
    }
    
    let i = 0
    
    if (i < 3) {
      let i = 'foo'
      console.log(i)
    }
    
    i++
    
    if (i < 3) {
      let i = 'foo'
      console.log(i)
    }
    
    i++
    
    if (i < 3) {
      let i = 'foo'
      console.log(i)
    }
    
    i++
    
    • let和const不会变量提升
    console.log(foo) // undefined
    var foo = 'jal'
    
    console.log(foo1) // ReferenceError
    let foo1 = 'jal'
    
    • 编码建议:不用var,主用const,搭配let

    3.数组的解构

    方括号[]中的便变量按顺序匹配数组元素

    const arr = [1, 2, 3]
    const a = arr[0]
    const b = arr[1]
    const c = arr[2]
    console.log(a, b ,c) // 1 2 3
    
    const [a, b, c] = arr
    console.log(a, b ,c) // 1 2 3
    
    const [, , c] = arr
    console.log(c) // c
    
    const [a, ...c] = arr // 三个点解构只能用于最后一个位置
    console.log(c) // [ 2, 3 ]
    
    const [a] = arr
    console.log(a) // 1
    
    const [a, b, c, d] = arr
    console.log(d) // undefined
    
    const [a, b, c = 123, d = 'defaultValue'] = arr
    console.log(c, d) // 3 defaultValue
    
    const path = 'a/b'
    const [, b] = path.split('/')
    console.log(b) // b
    

    4.对象的解构

    const obj = {name: 'yibo', age: 22}
    const {name} = obj
    console.log(name) // yibo
    
    const name = 'jal'
    const {name} = obj
    console.log(name) // SyntaxError: Identifier 'name' has already been declared
    
    const name = 'jal'
    const {name: objName, sex = 'boy'} = obj
    console.log(objName, sex) // yibo boy
    

    5.模板字符串

    反引号中间的${}为JS变量JS表达式

    const name = 'jack'
    const str = `this is ${name}`
    console.log(str)  //this is jack
    

    6.模板字符串标签函数

    const str = console.log`hello world` // [ 'hello world' ]
    
    const name = 'tom'
    const gender = true
    function myTagFunc (str, name, gender) {
      console.log(str, name, gender)  // [ 'hey, ', ' is a ', '.' ] tom true
      return str[0] + name + str[1] + gender + str[2]
    }
    
    const result = myTagFunc`hey, ${name} is a ${gender}.`
    console.log(result) // hey, tom is a true.
    

    7.字符串方法

    • .includes()
    • .startsWith()
    • .endsWith()
    const message = 'Error: foo is not undefined.'
    
    console.log(
      message.startsWith('Error'),  //true
      message.endsWith('undefined.'), //true
      message.includes('foo') //true
    )
    

    8.函数默认值

    // 带有默认值的参数要放在最后
    function foo(enable = true) {
      console.log(enable)
    }
    foo(true) //true
    foo(false) //false
    foo() // true
    

    9.剩余参数

    剩余参数只能出现在形参的最后一位,而且只能使用一次

    // function foo(){
    //   console.log(arguments)
    // }
    // foo(1, 2, 3) // [Arguments] { '0': 1, '1': 2, '2': 3 }
    
    
    // ...args只能出现在形参的最后一位,而且只能使用一次
    function foo(...args) {
      console.log(args)
    }
    foo(1, 2, 3) // [ 1, 2, 3 ]
    

    10.展开数组

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

    11.箭头函数

    const inc = n => n + 1
    console.log(inc(1)) // 2
    
    const sum = (a, b) => {
      return a + b
    }
    console.log(sum(1, 2)) // 3
    
    • 箭头函数不会改变this的指向,this为上层作用域的this
    const person = {
      name: 'tom',
      sayHi: function () {
        // this 是 person
        console.log(`hi, my name is ${this.name}`)
      },
      sayHiAync: function () {
        setTimeout(function () {
          //node.js: this 是 {}
          console.log(` sayHiAync: hi, my name is ${this.name}`)
        }, 1000);
      }
    }
    person.sayHi() // hi, my name is tom
    person.sayHiAync() // sayHiAync: hi, my name is undefined
    
    const person = {
      name: 'tom',
      sayHi:  () => {
        // node.js: this是{}
        console.log(`hi, my name is ${this.name}`)
      },
      sayHiAync: function () {
        setTimeout(() => {
          // this是person
          console.log(` sayHiAync: hi, my name is ${this.name}`)
        }, 1000);
      }
    }
    person.sayHi() // hi, my name is undefined
    person.sayHiAync() // sayHiAync: hi, my name is tom
    

    12.对象字面量增强

    • 属性名和属性值相同时可以省略,只写属性名
    • 对象方法可以直接写函数形式:method(){}
    • 使用方括号[]的方式计算动态属性名
    const bar = 111
    const obj = {
      foo: 123,
      // bar: bar,
      bar, // 同上一行效果
      // method1: function () {
      //   console.log(`method1: ${this}`)
      // },
      method2 () {
        // 直接写一个方法,同上面的冒号属性
        console.log(`method2: ${this}`)
      },
      [Math.random()]: 123, // 计算属性名
    
    }
    console.log(obj) // { foo: 123, bar: 111, method2: [Function: method2], '0.13076137144987743': 123 }
    

    13.对象扩展方法

    • Object.assign(target,source):将对各元对象中的属性复制到一个目标对象中
    // Object.assign 用第二个参数的对象属性覆盖第一个参数的对象。返回结果为第一个对象
    const source1 = {
      a: 123,
      b: 456
    }
    const source2 = {
      a: 333,
      c: 33
    }
    const target = {
      a: 11,
      b: 22
    }
    
    const result = Object.assign(target, source1, source2)
    console.log(result) // { a: 333, b: 456, c: 33 }
    console.log(result === target) // true
    
    function fun(obj) {
      // obj.name = 'function.obj'
      // console.log(obj)
      const funObj = Object.assign({}, obj)
      funObj.name = 'function.obj'
      console.log(funObj)
    }
    
    const obj = {
      name: 'global obj'
    }
    
    fun(obj)
    console.log(obj)
    
    • Object.is和===的比较,+0不等于-0,NAN等于NAN
    // Object.is 
    console.log(
      0 === false, // false
      0 == false, // true
      +0 ===-0, // true
      NaN === NaN, // false
      Object.is(+0, -0), // false
      Object.is(NaN, NaN) // true
    )
    

    14.代理对象:Proxy

    ES5中有一个Object.defineProperty,Vue2就是通过这个实现数数据双向绑定
    ES6中提供了Proxy,可以监视对象的读写过程,Vue3.0通过Proxy实现数据绑定

    // Proxy 
    const person = {
      name: 'jal',
      age: 20
    }
    
    const personProxy = new Proxy(person, {
      // 参数为目标对象、属性名
      get (target, property) {
        return property in target ? target[property]: 'default'
        // console.log(target, property) // { name: 'jal', age: 20 } name
        // return 100
      },
      // 参数为目标对象、属性名、属性值
      set (target, property, value) {
        if(property === 'age') {
          if(!Number.isInteger(value)) {
            throw new TypeError(`${value} is not an int`)
          }
        }
        console.log(target, property, value) // { name: 'jal', age: 20 } gender true
      }
    })
    
    personProxy.gender = true
    // personProxy.age = '11' // TypeError: 11 is not an int
    
    personProxy.age = 11
    
    // console.log(personProxy.name) // 100
    console.log(personProxy.name) // jal
    console.log(personProxy.xxx) // default
    

    Proxy对比Object.definePrperty

    • Object.defineProperty只能监听属性的读写
    • Proxy能监视更多对象操作:delete
    const person = {
      name: 'jal',
      age: 20
    }
    const personProxy = new Proxy(person, {
      deleteProperty(target, property) {
        console.log('delete', property) // delete age
        delete target[property]
      }
    })
    delete personProxy.age
    
    • Proxy更好的支持数组对象的监视(Vue重写数组的操作方法,劫持方法调用过程)
    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
    

    15.Reflect统一的对象操作API

    Reflect属于静态类(如Math),不能通过new来实例化对象,只能调用类的静态方法,如Reflect.get().Reflect内部封装了一系列对对象的底层操作。Reflect成员方法就是Proxy处理对象的默认实现

    const proxy = new Proxy(obj, {
      get(target, property) {
        // 不写get逻辑,相当于调用Reflect.get(target, property)。
        return Reflect.get(target, property)
      }
    })
    

    Reflect统一提供一套用于操作对象的API

    const obj = {
      foo: '111',
      bar: 'rrr',
      age: 18
    }
    // console.log("age" in obj)
    // console.log(delete obj['bar'])
    // console.log(Object.keys(obj))
    
    console.log(Reflect.has(obj, 'name')) // false
    console.log(Reflect.deleteProperty(obj, 'bar')) // true
    console.log(Reflect.ownKeys(obj)) // [ 'foo', 'age' ]
    

    16.Promise

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

    17.类 关键词 Class

    // function Person(name) {
    //   this.name = name
    // }
    // Person.prototype.say = function() {
    //   console.log(`hi, my name is ${this.name}`)
    // }
    // const p = new Person('jal')
    // p.say() // hi, my name is jal
    
    class Person {
      constructor(name) {
        this.name = name
      }
      say () {
      console.log(`hi, my name is ${this.name}`)
      }
    }
    const p = new Person('jal')
    p.say() // hi, my name is jal
    
    • 静态方法,this指向当前类型,而不是实例
    class Person {
      constructor(name) {
        this.name = name
      }
      say () {
      console.log(`hi, my name is ${this.name}`)
      }
      static create(name) {
        // this 指向当前类型,而不是实例
        console.log(this) // [Function: Person]
        return new Person(name)
      }
    }
    
    const tom = Person.create('tom')
    tom.say() // hi, my name is tom
    
    • 继承,关键词 extends
    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()
    // hi, my name is jack
    // my school number is 100
    

    18.数据解构Set

    // Set 数据结构
    const s = new Set()
    s.add(1).add(2).add(3).add(4).add(2)
    console.log(s) // Set(4) { 1, 2, 3, 4 }
    
    s.forEach(i => console.log(i)) // 1 2 3 4
    
    for(let i of s) {
      console.log(i)
    }
    // 1 2 3 4
    
    console.log(s.size) // 4
    
    console.log(s.delete(3)) // true
    console.log(s) // Set(3) { 1, 2, 4 }
    
    const arr = [1, 2, 1, 3, 4 ,1]
    const result = new Set(arr)
    console.log(result) // Set(4) { 1, 2, 3, 4 }
    const arr2 = Array.from(result)
    console.log(arr2) // [ 1, 2, 3, 4 ]
    const arr3 = [...result]
    console.log(arr3) // [ 1, 2, 3, 4 ]
    

    19.数据解构Map

    Map映射任意类型之间的关系,Map可以用任意对象作为键,而对象只能用字符串作为键

    // Map 映射任意类型之间的关系. Map可以用任意对象作为键,而对象只能用字符串作为键
    const obj = {}
    obj[true] = 'value'
    obj[1] = '11'
    obj[{a: 1}] = '33'
    console.log(Object.keys(obj)) // [ '1', '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
    

    20.原始数据类型Symbol

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

    const s = Symbol()
    console.log(s) // Symbol()
    console.log(typeof s) // symbol
    
    console.log(Symbol() === Symbol()) // false
    
    console.log(Symbol('foo')) // Symbol(foo)
    console.log(Symbol('bar')) // Symbol(bar)
    console.log(Symbol('baz')) // Symbol(baz)
    
    const obj = {}
    obj[Symbol()] = 111
    obj[Symbol()] = 2
    console.log(obj) // { [Symbol()]: 111, [Symbol()]: 2 }
    
    
    const name = Symbol()
    const person = {
      [name]: 'jal', // 作为私有成员防止被访问
      say(){
        console.log(this[name])
      }
    }
    person.say()// jal
    console.log(person[Symbol()]) // undefined
    // console.log(person[name]) // jal
    

    截止到ES2019一共定义了6种原始类型,和一个object类型,未来还会增加一个bigint的原始类型(stage-4阶段)标准化过后就是8中数据类型
    boolean(布尔)、symbol、number()、string(字符串)、undefined(未定义)、null(空)、object(对象)、bigint

    const s1 = Symbol.for('foo')
    const s2 = Symbol.for('foo')
    console.log(
      s1 === s2, // true
    // Symbol.for的参数会被转化为字符串
    Symbol.for(true) === Symbol.for('true'), // true
    )
    const obj2 = {
      // 为对象实现迭代器时会用到
      [Symbol.toStringTag]: 'XObject'
    }
    console.log(obj2.toString()) // [object Object] [object XObject]
    
    const obj3 = {
      [Symbol()]: 'symbol value',
      foo: 'normal value'
    }
    for(var key in obj3) {
      console.log(key)
    }
    // foo
    
    console.log(Object.keys(obj3)) // [ 'foo' ]
    console.log(JSON.stringify(obj3)) // {"foo":"normal value"}
    
    console.log(Object.getOwnPropertySymbols(obj3)) // [ Symbol() ]
    

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

    // for ... of 循环, 可以使用break
    const arr = [1, 2, 3, 4]
    for (const item of arr) { // item为每个对象实例
      console.log(item)
    }
    // 相当于
    // arr.forEach(item => {
    //   console.log(item)
    // })
    

    可以使用break终止循环

    // arr.forEach ,但是这个方法不能终止遍历
    // 为了终止遍历,我们之前,我们曾使用
    // arr.some() 返回true
    // arr.every() 返回false
    
    for(const item of arr) {
      console.log(item) 
      if(item > 1)break
    }
    

    遍历集合Set

    const s = new Set(['foo', 'bar'])
    for(const item of s) {
      console.log(item)
    }
    // foo bar
    

    遍历集合Map

    const m = new Map()
    m.set('foo', '123')
    m.set('bar', '34')
    
    for(const item of m) {
      console.log(item)
    }
    // [ 'foo', '123' ]  [ 'bar', '34' ]
    
    // 解构键和值
    for(const [key, value] of m) {
      console.log(key,value)
    }
    // foo 123
    // bar 34
    

    遍历对象,报错:TypeError:obj is not iterable

    const obj = {name: 'jal', age: 22}
    
    for(const item of obj) {
      console.log(item) // TypeError: obj is not iterable
    }
    

    ES中能够表示有结构的数据类型越来越多

    22.Iterable接口(可迭代接口)

    实现Iterable解构就是for...of的前提

    • 实现可迭代接口
    // 迭代器 iterator 
    const set = new Set(['foo', 'bar', 'baz'])
    
    const iterator = set[Symbol.iterator]()
    
    // 这就是for... of 循环实现的工作原理
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())
    console.log(iterator.next())
    // { value: 'foo', done: false }
    // { value: 'bar', done: false }
    // { value: 'baz', done: false }
    // { value: undefined, done: true }
    
    • 实现迭代器原理
    // obj 实现可迭代接口 Iterable
    const obj = {
      // iterator 方法
      [Symbol.iterator]: function () {
        // 迭代器接口 iterator 
        return {
          // 必须要有next方法
          next: function () {
            // 迭代结果接口 IterationResult
            return {
              value: 1,
              done: true
            }
          }
        }
      }
    }
    
    • 具体实现
    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)
    }
    // foo
    // bar
    // baz
    

    上面就是涉及模式中的迭代器模式、
    小案例:

    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(function (item) {
      console.log(item)
    })
    console.log('---------')
    for(const item of todos) {
      console.log(item)
    }
    // 吃饭
    // 睡觉
    // 打豆豆
    // 语文
    // 数学
    // 英语
    // 喝茶
    // ---------
    // 吃饭
    // 睡觉
    // 打豆豆
    // 语文
    // 数学
    // 英语
    // 喝茶
    

    22.生成器函数generator

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

    function * foo () {
      console.log('zce')
      return 100
    }
    // 这个foo就是一个Generator函数
    
    const result = foo()
    console.log(result)// Object [Generator] {}
    console.log(result.next())
    // zce
    // { value: 100, done: true }
    // 可以看出生成器对象实现了Iterator接口
    

    配合yield关键词使用

    生成器函数会返回一个生成器对象,调用这个生成器对象的next方法,才会让函数体执行,一旦遇到了yield关键词,函数的执行则会暂停下来,next函数的参数作为yield结果返回,如果继续调用函数的next函数,则会再上一次暂停的位置继续执行,直到函数体执行完毕,next返回的对象done就变成true

    function * fn () {
      console.log(111)
      yield 100
      console.log(222)
      yield 200
      console.log(333)
      yield  300
    }
    
    const generator = fn()
    
    console.log(generator.next())
    // 111
    // { value: 100, done: false }
    console.log(generator.next())
    // 222
    // { value: 200, done: false }
    console.log(generator.next())
    // 333
    // { value: 300, done: false }
    

    案例1:发号器

    // Generator 应用: 发号器
    
    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())
    // { value: 1, done: false }
    // { value: 2, done: false }
    // { value: 3, done: false }
    // { value: 4, done: false }
    // { value: 5, done: false }
    

    案例2:

    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 item of todos) {
      console.log(item)
    }
    // 吃饭
    // 睡觉
    // 打豆豆
    // 语文
    // 数学
    // 英语
    // 喝茶
    

    23.ES Modules

    语言层面的模块化标准

    二、ESMAScript 2016

    1.数组的includes方法

    const arr = ['foo', 1, false, NaN]
    // 以前使用indexOf, 存在则返回下标,不存在则返回-1, 缺点是无法判断NaN
    console.log(arr.indexOf('foo')) // 0
    console.log(arr.indexOf(1)) // 1
    console.log(arr.indexOf(false)) // 2
    console.log(arr.indexOf(NaN)) // -1
    
    console.log(arr.includes('foo')) // true
    console.log(arr.includes(1)) // true
    console.log(arr.includes(false)) // true
    console.log(arr.includes(NaN)) // true
    

    2.指数运算符**

    console.log(2 ** -52) // 2.220446049250313e-16
    

    三、ESMAScript 2017

    1.Object.values(obj)

    获取对象所有值的数组

    const obj = {
      name: 'jal',
      age: 20
    }
    // 对象的值组成的数组
    console.log(Object.values(obj)) // [ 'jal', 20 ]
    

    2.Object.eentries(obj)

    获取对象的键值数组

    // 对象的键值数组, 可以for...of 这个对象了
    console.log(Object.entries(obj)) // [ [ 'name', 'jal' ], [ 'age', 20 ] ]
    for (const [key, value] of Object.entries(obj)) {
      console.log(key, value)
    }
    // name jal
    // age 20
    

    3.Object.getOwnPropertyDescriptors(obj)

    获取对象的详细描述

    const p1 = {
      firstName: 'Ji',
      lastName: 'Ailing',
      get fullName() {
        return this.firstName + ' '+ this.lastName
      }
    }
    
    const p2 = Object.assign({}, p1)
    p2.firstName = 'zce'
    console.log(p2) // { firstName: 'zce', lastName: 'Ailing', fullName: 'Ji Ailing' }
    const descriptors = Object.getOwnPropertyDescriptors(p1)
    console.log(descriptors)
    /*
    {
      firstName: { value: 'Ji', writable: true, enumerable: true, configurable: true },
      lastName: {
        value: 'Ailing',
        writable: true,
        enumerable: true,
        configurable: true
      },
      fullName: {
        get: [Function: get fullName],
        set: undefined,
        enumerable: true,
        configurable: true
      }
    }
    */
    
    const p3 = Object.defineProperties({}, descriptors)
    p3.firstName = 'zce'
    console.log(p3.fullName) // zce Ailing
    

    4.padEnd/padStart

    用指定字符串填充目标字符串的头部或者尾部,直到达到指定的长度为止

    const books = {
      html: 5,
      css: 16,
      javascript: 128
    }
    for(const [key, value] of Object.entries(books)) {
      console.log(key, value)
    }
    // html 5
    // css 16
    // javascript 128
    
    for(const [key, value] of Object.entries(books)) {
      console.log(`${key.padEnd(16, '-')}|${value.toString().padStart(3, '0')}`)
    }
    // html------------|005
    // css-------------|016
    // javascript------|128
    

    5.在函数参数中添加尾逗号

    function foo (
     bar, 
     baz,
    ) {
      
    }
    
    const arr = [
      10,
      20,
      30,
    ]
    

    6.Async / Await

    Promise的语法糖,解决了回调地狱的问题

    相关文章

      网友评论

          本文标题:笔记三:ECMAScript新特性

          本文链接:https://www.haomeiwen.com/subject/gmfglktx.html