• module4-05-ES6新特性


    ES6新特性

    一、ECMAScript的发展过程与简介

    • ES6是2015年的新标准,要搞清楚ES6是泛指还是特指。特指的话是ES2015,泛指的话是指ES2015之后的版本

    1.1 ES6概述

    • 解决原有语法上的一些问题或者缺陷(作用域问题)

    • 对原有语法进行增强(解构、展开、模板字符串)

    • 全新的对象、全新的方法、全新的功能(Promise、proxy、Object.asgn...)

    • 全新的数据类型和数据结构(Symbol、Set、Map)

    1.2 学习ES2015准备工作

    • 准备一个支持es6的环境,如chrome最新版本或者nodejs

    • 可以在VScode软件中使用Chrome调试

      • 需要安装的插件

        • ① Debugger for Chrome

        • ② Live Server

        • ③ Browser Preview

      • 查看右下角port

      • 点击左上角运行-启动调试-chrome

      • 然后修改配置文件的端口为右下角port一致,在网页里面选择对应文件即可

    二、ECMAScript2015 的新特性

    2.1 let、const、块级作用域

    • 举一个例子,常见的for循环循环变量污染问题

    var arr = []
    for (var i = 0; i < 5; i++) {
       arr[i] = function () {
           console.log(i)
      }
    }
    arr[0]() // 5
    arr[1]() // 5
    • 因为在ES6之前只有函数作用域和全局作用域,所以循环定义的i会暴露到全局也就是window里面,arr存的函数输出自然就是循环完之后的i值了

    (1)块级作用域

    • 讲let与const之前不得不介绍块级作用域

    • 如:if语句中使用let跟var变量是不一样的

    • 可以把一个{}里面看成是块级作用域

    • 继续用for循环来举例

      • const arr = []
        for (let i = 0; i < 5; i++) {
           arr[i] = function () {
               console.log(i)
          }
        }
        arr[0]() // 0
        arr[1]() // 1
    • 说明在函数里面是有两层作用域的,所以可以把for循环看为

      • {
        let temp = 0,
        end = 5
        if (temp < end) {
        let i = temp
        arr[i] = function () {
        console.log(i)
        }
        temp++
        }
        if (temp < end) {
        let i = temp
        arr[i] = function () {
        console.log(i)
        }
        temp++
        }
        //...
        }
        arr[0]()
        arr[1]()

    (2)let与const

    • ① 他们不会有变量提升

    • 后面声明了但是前面引用会有暂时性死区

    • const声明的变量指向的地址不可变,复杂数据类型可以继续修改属性,相当于let的只读效果

    • 最佳用法

      • 不用var,朱永const,配合let

    2.2 数组与对象的解构

    (1)数组的解构

    • ① 下面foo1写在赋值符左端的数组的第一个位置,代表把赋值符右边数组第一个下标的值传给foo1

    • ...语法是代表把剩余的参数全部放进一个数组中,如果后面没有参数了会返回一个空数组

    • ③ foo2中设置了默认值,即右边没有匹配的值才会使得foo2 = 1000

      • const arr = [100, 200, 300, 400]
        const [foo1, foo2 = 1000, ...rest]
        console.log(foo1) // [100]
        console.log(foo2) // [200]
        console.log(rest) // [300, 400]
      •  

    (2)对象的解构

    • 使用方法跟数组解构类似,不同的是设置变量的命名问题

    • 设置变量命名需要与原函数的key值相等,但是会产生命名冲突(这个名字原先被声明过了)

      • 可以使用 : 符号获取这个符号左边的值然后声明 : 右边的变量,设置默认值如果有:则在右边用=来设置

      • const obj = {
           name: 'zs'
        }
        const name = 'ls'
        const { name: newName = 'ww' } = obj
        console.log(newName) // 'zs'
      •  

    2.3 模板字符串、模板字符串标签函数

    (1)模板字符串

    • 相对于普通的字符串模板字符串使用的是``来定义

    • 在这里面可以让字符串换行

    • 原本的字符串插值要用几个字符串拼接在一起,而模板字符串可以用插值表达式${},在这里面可以放变量,表达式,甚至调用函数

    • ex:

    const name = 'foo'
    const str = `hello, ${name}, ${1 + 1}`
    // 'hello, foo, 2

    (2)模板字符串标签函数

    • 模板字符串标签函数可以看成是用``代替函数调用符(),并传入参数

    • ex:

    const name = 'zs'
    const gender = true
    function foo (strings, name, sex) {
       sex = sex ? 'man' : 'woman' // 可以在里面进行操作
       console.log(strings) // ['hi, ', ', ', '']
       console.log(name) // 'zs'
       console.log(sex) // ’man‘
    }
    foo`hi, ${name}, ${gender}`
    • 上面的例子表示模板字符串会被${}分割并组成一个数组,如果${}在开头或者结尾,则默认会多出一个空字符串 ‘’

    • 然后第二个参数开始表示插值表达式传入的值${},设置形参的时候不用名字相同

    2.4 字符串扩展方法

    • includes():表示字符串是否包含某字符串

    • startsWith():表示字符串开头是否是某字符串

    • endWith():表示字符串结尾是否是某字符串

    2.5 参数默认值

    • 我们平时为函数形参赋予默认值的时候是使用value = value || true其实是不严谨,比如如果传入一个false也会进行默认值赋值操作

    • 正确的做法是判断是否严格等于undefinedvalue = value === undefined ? true : value

    • 而ES6可以在定义形参的时候就设置好默认值

      • 不过设置默认值的形参建议放在参数列表的后面

    function foo (value1 = 'value1', value2 = 'value2') {
       console.log(value1, value2)
    }
    foo('haha') // 'haha' 'value2'

    2.6 ...操作符

    • 主要有两种作用,作剩余操作符展开操作符

    (1)剩余操作符

    • 用作于获取的时候使用,使用特点是必须要放在最后一位,

    • ex:获取函数的参数

    function foo (a, ...rest) {
        console.log(rest)
    }
    foo(1, 2, 3, 4)
    // [2, 3, 4]

    (2)展开操作符

    • 用作于传递数组的每一项作为参数,特点也是放在最后,可以传入伪数组

    • ex:传入函数参数并将arguments转换为数组

    var arr = [1, 2, 3]
    function foo () {
        console.log([...arguments])
    }
    foo(...arr)
    // [1, 2, 3]

    2.7 箭头函数

    • 箭头函数只能作为匿名函数,可以用函数表达式声明,建议用在传递的参数是函数上面

    (1)箭头函数的语法

    // 普通函数
    const foo1 = function (a, b) {
        return false
    }
    // 箭头函数
    // ① 当代码段只有一条表达式的时候
    const foo2 = (a, b) => return false
    // ② 当参数只有一个的时候
    const foo2 = a => return false
    const foo2 = (a, b) => {
        console.log(a, b)
        return false
    }

    (2)this指向

    • 不会指向调用者,会指向定义时候所在的this

    • 若想使用4种可以改变this的函数调用方法的话(构造函数调用、对象方法调用、事件绑定方法、定时器函数),建议先用普通函数包裹一个子调用的箭头函数,如下

    var age = 20
    const obj = {
        age: 18,
        foo1: () => {
            // 这里面的this指向的是foo1所在的this,因为obj没有块级作用域,所以指向window,而window.age = 20
            console.log(this.age)
        },
        foo2: function () {
            // 这里面的this被普通匿名函数给锁住了,在调用的时候只想调用者也就是obj,而obj.age = 18
            ;(() => {
                console.log(this.age)
            })();
        }
    }
    obj.foo1() // 20
    obj.foo2() // 18

    2.8 对象字面量加强与计算属性名

    (1)对象字面量加强

    • 可以理解为ES5对象写法的语法糖

    • 属性值与方法的语法糖:

      • 注意:下面的sayHi实质是普通函数的简写,不是箭头函数

      • let name = 'zs'
        const obj = {
            name,
            sayHi () {
            	console.log(this.name)
            }
        }
        obj // { name: 'zs', sayHi: f }

    (2)计算属性名

    • 在定义对象属性值或者调用的时候(在ES6之前只能调用使用)可以使用 [] 包裹的里面用表达式计算出属性名

      • 如果是全局变量的函数,需要用window[]来计算变量名调用

    let fnName = 'foo'
    const obj = {
        [fnName] () {
            console.log(`我的函数名叫做${arguments.callee.name}`)
        }
    }
    // obj[fnName]也可以是ES6之前就可以实现的
    obj.foo()
    function foo () {
        console.log('我是foo函数调用的')
    }
    // [fnName]()这种方法是错误的
    window[fnName]()

    2.9 Object新增方法

    • 接下来介绍一下Object.assign()Object.is()

    (1)Object.assgin()

    • 这个是用于对象的浅拷贝的,下面我会介绍几种应用场景

    • 语法:

      • const result Object.assign(obj1, obj2)
        result === obj1 // true
        // obj2的值会覆盖到obj1, 有同名字的key就会
        // 即使不使用返回值, obj1也已经发生改变了
    • 当传入参数是引用类型而且不想函数内部引用会修改函数外部的值

      • 但是如果里面的值还有引用类型的话,需要深拷贝才可而已

    const obj = {
        name: 'zs',
        oobj: {
        aaa: 1
    }
    }
    function foo (obj) {
        const newObj = Object.assign({}, obj)
        newObj.name = 'ls'
       	newObj.oobj.aaa = 2
        console.log(obj)
        console.log(newObj)
    }
    foo(obj)
    // 下面oobj.aaa都改成了2
    // obj --- { name: 'zs', oobj: { aaa: 2 } }
    // newObj --- { name: 'ls', oobj: { aaa: 2 } }
    • ② 构造函数传入参数的时候,将参数的值传入到this中

    function Student (options) {
        Object.assign(this, options)
    }

    (2)Object.is()

    • 这个用于更精确的全等===,但还是推荐是用===

    • ① 比如 -0 与 +0 使用全等符号是相等的,但是我们期望它不等

      • console.log(-0 === +0) // true
        console.log(Object.is(-1, +0)) // false
    • ② NaN与NaN是不全等的,也不等于,在这个方法里面会返回true

      • console.log(NaN === NaN, NaN == NaN) // false false
        console.log(Object.is(NaN, NaN))

    2.10 class类、静态方法、类的继承

    (1)class类

    • 可以当成是构造函数的语法糖,在一个class里面设置完

    • 语法:

      • constructor相当于定义构造函数的代码段

      • 里面的方法比如sayHi是设置在prototype里面的

    class Person {
        constructor (name, age) {
            this.name = name
            this.age = age
        }
        sayHi () {
            console.log(`Hi, my name is ${this.name}`)
        }
    }

    (2)静态方法

    • 构造函数有静态方法和实例方法,静态方法是构造函数对象调用的。实例方法,是实例对象调用的

    • 语法:

      • 静态方法里面的this指向的是构造函数对象

      • 甚至可以直接new this()调用

    class Person {
        constructor (name, age) {
            this.name = name
            this.age = age
        }
        sayHi () {
            console.log(`Hi, my name is ${this.name}`)
        }
        static create (name. age) {
            console.log(this)
            return new Person(name, age)
            // return new this(name, age) 也是正确的
        }
    }

    (3)类的继承

    • 类似于组合继承,不过有一个super方法可以根据我们的需求赋值

    • 语法:

      • 主要是使用super来继承父类的属性添加到实例对象中,super是一定要调用的

    class Person {
        constructor (name, age) {
            this.name
            this.age
        }
        sayHi () {
            console.log(`Hi, my name is ${this.name}`)
        }
    }
    // 继承
    class Student extends Person {
        constructor (name, age, number) {
            super(name, age) // 一定要调用
            this.number = number
        }
        hello () {
            console.log()
        }
    }
    • 其中用super继承属性是存在于实例对象中

    • 继承的方法是存在于__proto__.__proto__之中

    • 定义的方法存在于__proto__之中

    2.11 Set

    • Set是一种新的数据结构,叫做集合

    • 它可以像数组一样存储变量、赋值。可以用来作数据去重

    const s = new Set()
    s.add(1).add(2).add(3).add(4)
    console.log(s) // Set(4) {1, 2, 3, 4}

    Set的属性和方法

    s.add() // 添加值
    s.forEach() // 与数组的同理
    s.size // 相当于length
    s.has() // 是否含有某一项
    s.delete // 删除某一项
    • 里面有遍历器,可以使用for of遍历

    • 也可以转换成数组运算

      • ① Array.from()

      • ② [...s]

    2.12 Map

    • Map是一种新的数据结构,叫做字典

    • 它跟对象的用法非常相像,他的key可以是任意类型的数据而object会把数据都变成string类型再传入到key

    Map的属性和方法

    const map = new Map()
    const a = { a: 1}
    map.set(a, 100)
    console.log(map) // Map(1) {{a: 1} => 100}
    console.log(map.get(a)) // 100
    // 方法与Set类似
    map.has()
    map.delete()
    map.clear()
    map.forEach()
    ...

    2.13 Symbol

    • 是ES2015的一种新的基本数据类型

    • 可以优秀的解决命名冲突问题,为对象添加私有变量(外部访问不到)

    • 语法:

      • ① Symbol()

      • ② Symbol(参数)

    const s = Symbol('123')
    const b = Symbol('123')
    s === b // false

    Symbol的一些用法

    • ① 传入字符串识别是否为同一Symbol(Symbol.for)

      • 传入的如果不是字符串,会转换成字符串再传入

      • 区分是否是for方法,即使传入的字符串相同不都是for方法的话不会返回同一个值

    const s1 = Symbol.for('123')
    const s2 = Symbol.for(123)
    const s3 = Symbol(’123‘)
    const
    console.log(s1 === s2) // true
    console.log(s2 === s3) // false
    • ② 作为对象的私有变量

      • 即使使用JSON转化,也会识别不出来,所以非常隐蔽

      • 可以使用Object.getOwnPropertySymbols()来获取,但是只能获取到symbol值

    const obj = {
        foo: 1,
        [Symbol()]: 'symbol'
    }
    console.log(obj) // {foo: 1}
    Object.keys(obj) // ['foo']
    JSON.stringify(obj) // {"foo":"1"}
    Object.getOwnPropertySymbols(obj) // [Symbol()]
    • ③ 为object类型的toString添加标识

      • 使用到了Symbol.toStringTag

      • 如果传入的value还是object类型则还是[object object]

    const obj1 = {}
    console.log(obj1.toString()) // [object object]
    const obj2 = {[Symbol.toStringTag]: 'hahaha'}
    console.log(obj2.toString()) // [object hahaha]

    2.14 for of 遍历

    • for if的出现是为了作为遍历所有数据结构的统一方式

    • 在次之前我们遍历的方式有

      • 对象:for in

      • 数组:forEach...

    • 这些方法都有一定的局限性只适用于局部数据

      • 比如forEach不能用break打断

      • for in 不能遍历集合Set

    • for of可以遍历大部分数据类型包括set、map等,且相对于forEach可以设置break打断

    • 但是目前obj还是能适配for of遍历

    const s = new Set(['foo1', 'foo2' ,'foo3'])
    for (const item of s) {
        console.log(item)
    }
    // foo1 foo2 foo3
    const m = new Map()
    m.set('a', 1)
    m.set('b', 2)
    for (const item of m) {
        console.log(item)
    }
    // 会变成数组形式来表现Map的对应关系
    // ['a', 1] ['b', 2]

     

    2.15 ES2015的其它内容

    • 可迭代接口

    • 迭代器模式

    • 生成器

    • Proxy 代理对象

    • Reflect 统一的对象操作API

    • Promise 异步解决方法

    • ES Modules 语言层面的模块化标准

    2.16 ES2016概述

    • 主要增加了两个新特性数组的includes,指数运算符

    (1)Array.prototype.includes

    • 类似于ES2015中String的includes如果含有则返回true

    • 它比indexOf的优点之一在于可以检测是否含有NaN

    const arr = [1, 2, 3, NaN, 5]
    console.log(arr.indexOf(NaN)) // -1
    console.log(arr.includes(NaN)) // true

    (2)**指数运算符

    • 在这之前,指数运算是多个相乘或者Math.pow()实现的

    console.log(Math.pow(2, 3)) // 8
    console.log(2 ** 3) // 8
  • 相关阅读:
    杯具,丢失了一部分邮件
    Android Building System 总结
    build/envsetup.sh
    PhoneApp是什么时候被创建的
    测试电信的WAP PUSH的方法
    修改Activity响应音量控制键修改的音频流
    ril崩溃时的出错地址定位
    java interface 强制类型转换小记
    android 修改系统程序图标大小
    git 合并patch的方法
  • 原文地址:https://www.cnblogs.com/lezaizhu/p/14236456.html
Copyright © 2020-2023  润新知