ES6新特性

254 阅读5分钟

这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战

let 与块级作用域

在 ES2015 (ES6) 之前,ES 只有前两种作用域:

• 全局作用域

• 函数作用域

• 块级作用域 (块,就是 {} 包裹起来的一个范围)

可以通过新的关键字 let 定义块内部的变量, let 定义的变量在块级作用域内部能够被访问. 非常适合设置 在 for 循环中的循环变量.

// 内外循环的i互不影响, 但尽量不要这么写
for (let i = 0 ; i < 3 ; i++) {
      for (let i = 0; i < 3;i++) {
        console.log(i);
      }
    }

注意: 和 var 有另外一个区别,let 不会进行变量声明提升

const 恒量 / 常量

特点:在 let 的基础上增加了一个【只读】效果, 也就是说: 声明的时候必须同时赋初值

    const name = "zs";

const声明的变量后期是不允许更改的:

    const name ;
    name = "zs"; //报错

不更改指针的指向, 只改变指针指向的对象的内容是允许的:

    const obj = {};
    obj.name = "zs"; //允许
    obj = {}; //不允许

最佳实践:不用 var,主用 const,配合 let

数组解构

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

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

    //提取foo后面的所有成员, 保存到rest这个数组里
    const arr = [100, 200, 300]
    const [foo, ...rest] = arr
    console.log(rest) // [200,300]

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

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

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

    const path = "foo/bar/baz"
    const [,,a] = path.split("/")
    console.log(a)  // baz

对象解构

    // 对象解构
    const obj = { name: 'zs', age: 18 }
    const { name } = obj
    console.log(name) //zs

    
    //对象中的name属性与变量名冲突, 使用newName作为新的属性名字
    const name = "tom"
    const { name: newName } = obj
    // const { name: newName = "jack" } = obj
    console.log(name) // tom
    console.log(newName) // zs


    const { log } = console
    log("haha")

字符串

模板字符串字面量

  1. 支持换行
  2. 支持使用差值字符串的方式嵌入一些值
    const str = `this 
    is a \`string`
    console.log(str)

image.png

    //支持使用差值字符串的方式嵌入一些值
    const name = "tom"
    const str = `hey, ${name}`
    console.log(str) //hey,tom

    
    const name = "tom"
    const str = `hey, ${name},${1 + 1},${Math.random()}`
    console.log(str)   //hey,tom,2.0.54564654

模板字符串标签函数

    // 模板字符串标签函数
    // const str = console.log`hello JavaScript`

    const name = "zs"
    const gender = true
    function myTagFunc(strings, name, gender) {
      // console.log(strings,name,gender)
      // 处理一下 性别
      const sex = gender ? "man" : "woman"
      return strings[0] + name + strings[1] + sex + strings[2]
    }
    const str = myTagFunc`hi, ${name} is a ${gender}`
    console.log(str) // hi, zs is a man 

字符串的扩展方法

• includes() //是否包含

• startsWith() //以...开头

• endsWith() //以...结尾

返回值是布尔值

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

参数默认值

    // 函数参数的默认值
    function foo(bar,enable = true) { //如果有多个参数, 参数默认值的部分需要放在后面
      //相当于 enable = enable === undefined ? true : enable
      console.log('foo invoked enable:')
      console.log(enable) //如果给enable传参了, 就使用参数值; 没传参就使用默认值
    }
    foo('bar')

剩余参数

arguments 会接收所有的实参, 放到一个类数组对象中

    // 剩余参数
    function fun(...args) { 
      console.log(args) // [1,2,3,4]
    }
    fun(1,2,3,4)


    function fun(n,...args) { // ...只能出现一次, 且只能在最后一位
      console.log(args) // [2,3,4]   1 传给了n
    }
    fun(1,2,3,4)

展开数组

    const arr = ['foo', 'bar', 'baz']
    // console.log(arr[0],arr[1],arr[2])
    // console.log.apply(console,arr) // 参数分别为: this的指向, 实参列表

    console.log(...arr) //foo bar baz 把数组一次展开, 可以传递给其他实参列表

箭头函数

    const plus = (a, b) => {
      console.log('plus invoked')
      return a + b
    }
    console.log(plus(1,2))  // 3

.

    const arr = [1,2,3,4,5,6,7]
    // const arr1 = arr.filter(function (item) {
    //   return item % 2  //结果为1, 结果为true, filter判断正确, 所以只能接收一些奇数
    // })
    
    const arr1 = arr.filter(i => i % 2)

如果函数体只有一条数据, 箭头函数会直接将结果作为返回值返回

箭头函数与 this

箭头函数内部没有this, 而是会在外面进行查找, 找不到的话就相当于查找失败:

const person = {
      name: "tom",
      sayHi: function () {
        console.log(`hi,my name is ${this.name}`)  //hi,my name is tom
      }

      sayHi: () => {
        console.log(`hi,my name is ${this.name}`)  //hi,my name is
      }
    }

有时候, 箭头函数中没有this反而是有好处的.

    const person = {
      name: "tom",
      sayHi: function () {
        // const _this = this;
        setTimeout(function (){
          console.log(`hi,my name is ${this.name}`)  //hi,my name is
        },1000);
      }
    }
    person.sayHi()

比如上面的代码中, setTimeout里执行的函数是在全局执行的, 它内部的this指的是window, window里没有name. 需要先记录一下当前的this才行:

      sayHi: function () {
        const _this = this; //记录一下sayHi方法里的this
        setTimeout(function (){
          console.log(`hi,my name is ${_this.name}`) //hi,my name is tom
        },1000);
      }

而在箭头函数中, 由于它本身没有this, 会自己向外面查找, 这时this指向的就是person.

    const person = {
      name: "tom",
      sayHi: function () {
        // const _this = this; //记录一下sayHi方法里的this
        setTimeout(()=>{
          console.log(`hi,my name is ${this.name}`) //hi,my name is tom
        },1000);
      }
    }
    
    person.sayHi()

对象字面量增强

  1. 当对象的属性名和值相同时, 可以只写一个属性名
  2. 对象内部的函数可以省略function
  3. 属性名可以使用任意的变量或表达式, 用[]表示
    const obj = {
      name: "tom",
      // bar: bar
      bar,
      sayHi () {
        console.log('hi')
        console.log(this)
      },
      // 计算属性名
      [age]: 18,
      [1+2]: 18
    }

对象扩展方法

Object.assign

将多个源对象中的属性复制到一个目标对象中, 如果属性相同, 源对象会覆盖目标对象.

Object.assign(目标对象target, 源对象source); 返回的值就是target自己. 例如:

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

多个源对象:

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

复制对象:

    // 复制对象
    function fun(obj) {
      // 希望内部更改时,不要改外部的对象
      const newObj = Object.assign({},obj)
      newObj.name = 'tom'      
      console.log(newObj)   // {name: 'tom', age: 18}
    }
    const obj = {
      name: 'jack',
      age: 18
    }
    fun(obj)
    console.log(obj)  // {name: 'jack', age: 18}

应用:

    // 应用,在 options 对象参数接收时,简化
    function Block(options) {
      // this.width = options.width;
      Object.assign(this,options)
    }
    const block1 = new Block({width: 100, height: 100, x: 50, y: 50})
    console.log(block1)

之前获取宽度, 高度等数据的时候需要使用this.width = options.width;等将options中的值一一赋给this, 现在可以直接用 Object.assign(this,options) 方法将options中所有的内容保存到this中, 简化了写法.

Object.is

比较两个数, 结果返回一个布尔值. 可以比较+0和-0, NaN, 得到的结果与全等不同:

    console.log(0 == false); //true
    console.log(0 === false); //false
    console.log(Object.is(0,false)); //false

    console.log( +0 === -0); //true

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

class 类

ES6中新增加了一个class来定义对象类型, 在此之前是通过构造函数或者构造函数原型对象的方法来定义对象的类型.

    class Person {
      constructor (name, age) {
        this.name = name;
        this.age = age;
      }
      sayHi () {
        console.log(`hi,my name is ${this.name}`)
      }
    }
    const p1 = new Person("tom",18)
    
    console.log(p1)
    p1.sayHi()

image.png

静态成员 static

ES2015 中新增添加静态方法的关键词 static

静态方法中的this指的是类型自己 (比如class Person{})

    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)  // class Person {...}
        return new Person(name,age)
      }     
    }
    const p1 = Person.create("zs",19)
    console.log(p1)   // Person {name: 'zs', age: 19}

类的继承

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

    class Student extends Person {
      constructor (name,age,number) {
        super(name,age)  //super指向的是父类, 从父类的constructor中继承name和age
        this.number = number
      }
      hello () {
        super.sayHi()
        console.log(`学号是 ${this.number}`)
      }
    }
    
    const s1 = new Student("tom",18,101)
    s1.hello();

image.png

Set 数据结构

内部成员不允许重复, 在实例内部存放数据

  • add() 方法返回的是集合本身
    const s = new Set()
    s.add(1).add(2).add(3).add(4).add(2)
    console.log(s)  // Set(4) {1, 2, 3, 4}

其他set的方法:

    const s = new Set()

    // 遍历1
    s.forEach(i => console.log(i))

    //遍历2
    for (let i of s) {
      console.log(i)
    }

    // 得到集合的数据长度
    console.log(s.size)

    //判断集合中是否存在某个值, 返回一个布尔值
    console.log(s.has(4))

    // 删除某个值, 删除成功返回true, 失败返回false
    console.log(s.delete(100))
    console.log(s)

    // 清除集合中的全部内容
    s.clear()
    console.log(s)

数组去重

法1 把元素由数据转成集合, 就去掉了数组中重复的元素:

    // 数组去重
    const arr = [1.3,4,6,2,4,7,5,8]
    const b = new Set(arr);
    console.log(b)  // Set(7) {1.3, 4, 6, 2, 7, …}, 因为把多余的元素删除了

把集合转成数组:

    const arr = [1.3,4,6,2,4,7,5,8]
    const b = Array.from(new Set(arr))  // 把集合转成数组
    console.log(b)  // (7) [1.3, 4, 6, 2, 7, 5, 8]

法2 利用展开符号...把集合展开, 每一项作为数组的元素:

    // 数组去重
    const arr = [1.3,4,6,2,4,7,5,8]
    const b = [...new Set(arr)]
    console.log(b)  // (7) [1.3, 4, 6, 2, 7, 5, 8]

Map 数据结构

与对象的区别:

  • map可以使用任意类型当做map的键
  • 对象只能使用字符串类型或者symbol类型当做键(属性名)

map的set和get方法:

    const map = new Map()
    const a = { a: 1}
    map.set(a,100)
    console.log(map)
    console.log(map.get(a))

image.png

其他方法:

    map.has()  //是否存在某个值
    map.delete()  //删除某个值
    map.clear()  //清空
    
    //遍历
    map.forEach((value,key) => {
      console.log(key,value)     //{a: 1} 100
    })

Symbol

一种全新的原始数据类型, 最主要的作用就是为对象添加独一无二的属性标识符

在对象中字符串和symbol定义的属性名的区别:

  • symbol定义的属性是不能在对象外调用的,相当于私有的 (原理是每个symbol都是不一样的, 所以不能互相调用); 但字符串定义的属性可以引用
    const obj = {
      [Symbol()] : 789,
      name: "zs"
    }
    obj[Symbol()] = 123
    obj[Symbol()] = 456
    
    console.log(obj[Symbol()])   //undefined
    console.log(obj.name)     //zs

创建和标识:

    //创建
    const s = Symbol()
    console.log(s)
    
    console.log(typeof s)
    
    console.log(Symbol() === Symbol()) //false, 说明每个都是独一无二的
    
    //标识symbol
    console.log(Symbol('foo'))
    console.log(Symbol('bar'))
    console.log(Symbol('baz'))

如何找到同一个symbol:

    const a = Symbol.for(foo)
    const b = Symbol.for(foo)
    console.log(a === b)   //true

for维护的是symbol和字符串之间的关系, 会把for()中的参数转换成字符串再判断是否相同:

    const a = Symbol.for(true)
    const b = Symbol.for('true')
    console.log(a === b)    //true

symbol中一些内置属性也可以作为标识符进行区分:

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

对象中symbol和字符串属性获取的区别:

    const obj = {
      [Symbol()]: "Symbol value",
      foo: "foo value"
    }
    // for (var k in obj) {  //只有foo, 拿不到symbol的值
    //   console.log(k)
    // }

    // console.log(Object.keys(obj))   //只有foo, 拿不到symbol数据
    // console.log(JSON.stringify(obj))  //拿不到symbol数据

    console.log(Object.getOwnPropertySymbols(obj))  //只能获取symbol类型, 不能获取字符串

for…of 循环

for..of遍历普通对象时有问题 , 最好不要用. 可以使用for...in

例1 遍历数组

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

    for (const item of arr) {  //可以使用break打断
      console.log(item)
    }

    arr.forEach(item => {   //没有办法打断遍历
      console.log(item)
    })

例2遍历集合set

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

例3 遍历map

    const m = new Map()
    m.set("foo",1)
    m.set("bar",2)
    for (const [key,value] of m) {
      console.log(key,value)   // foo 1     bar 2
    }

ECMAScript 2016

includes()

  • indexOf()不能检测NaN

  • includes()可以检测NaN, 如果存在返回true

    const arr = [1,true,NaN,23,'hello']
    
    // includes 包含
    console.log(arr.includes(NaN))  //true

指数运算符 **

    // 指数运算符 **
    // console.log(Math.pow(2,3))
    
    console.log(2 ** 10)