ES6新特性汇总

ES6改动很大,可以简单分为四类
1、解决原有语法的缺陷和不足
例如:let,const
2、对原有语法进行增强
解构、扩展、模板字符串
3、新增对象、全新的方法,全新的功能
Object.assign()、Proxy对象代理、Reflect 等等
4、全新的数据类型和数据结构
set、map、class、迭代器、生成器 等等

一、解决原有语法的缺陷和不足

1、let:块级作用域,没有变量提升

用途:for循环的计数器(for循环内部let声明的变量同样拥有块级作用域);循环事件绑定不必采用闭包形式

   for (let i=0; i<3; i++) {
      let i = 'foo'
      console.log(i)
    }

2、const: 恒量/常量;声明后不能修改内存地址,可修改属性成员

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

二、对原有语法的增强

1、数组的解构:根据数组对应的位置提取对应的值赋给对应的变量

let arr = [1,2,3]
let [x, y, z] = arr

如果未取到对应的值,赋值“undefined”
可以用...运算符

var [x, ...other] = arr

可以设置默认值

var [x='0', y, z] = arr

用途:字符串截取

const str = 'http://www.baidu.com?titile=article'
var [, strParam] = str.split('?')

2、对象的解构:根据属性名提取

const obj = {name: 'zdd', age: 18}
const { name, age } = obj

如果想换一个变量名&添加默认值

const {name: objName='www', age} = obj

应用场景:代码简化

const { log } = console
log('hh')

3、模板字符串:字符串增强

1、可换行
2、可使用插值表达式添加变量,变量也可以替换为可执行的js语句:

let str = `生成一个随机数:${ Math.random() }`

标签模板字符串,标签相当于一个自定义函数,自定义函数的第一个参数是被差值表达式截取的数组

    // 标签模板字符串
    const name = 'www';
    const isMan = true
    const tagFn = function (strings, name, isMan) {
      let sex = isMan ? 'man' : 'woman';
      return strings[0] + name + strings[1] + sex + strings[2]
    }
    const result = tagFn`hey, ${name} is a ${isMan}.`

4、字符串的扩展方法

1、includes
2、startWith
3、endsWith

5、函数参数增强:参数默认值

只有当参数为不传或传入undefined时使用默认值

    const fn = function (x=1, y) {
      console.log(x)
      console.log(y)
    }
    fn()

6、...操作符:收起剩余数据、展开数组

收取剩余参数:取代arguments,arguments是一个类数组,...操作符是一个数组类型,可以使用数组方法
1、仅使用一次
2、放在参数最后

    const fn = function (x, ...y) {
      console.log(y.slice(0))
    }
    fn(1,2,3,4,5)

展开数组

    const spredArr = [1,2,3,4]
    console.log(...spredArr)
    console.log.apply(this, spredArr) //es5代替方案

7、箭头函数:简化写法

箭头函数的this指向上级作用域

    const name = 'tony'
    const person = {
      name: 'tom',
      say: () => console.log(this.name),
      sayHello: function () {
        console.log(this.name)
      },
      sayHi: function () {
        setTimeout(function () {
          console.log(this.name)
        }, 500)
      },
      asyncSay: function () {
        setTimeout(()=>console.log(this.name), 500)
      }
    }
    person.say()  //tony
    person.sayHello() //tom
    person.sayHi() //tony
    person.asyncSay()  //tom

8、对象字面量的增强

1、如果key与value变量名相同,省略:value
2、省略函数:function
3、计算属性:[Math.random()]

   const bar = 'bar'
    const obj = {
      bar,
      fn () {
        console.log('1111')
      },
      [Math.random()]: '123'
    }
    console.log(obj)

三、新增对象、全新的方法、全新的功能

1、Object.assign():合并多个对象,第一个参数就是最终的返回值,如果对象的属性名相同,后面的覆盖前面的

用途:复制对象,给options属性赋默认值

    let objA = {
      a: 'aa',
      b: 'bb'
    }
    let objB = {
      b: 'dd',
      c: 'ee'
    }
    let result = Object.assign({}, objA, objB)
    result.a = 'cc'
    console.log(objA, result) //{a: "aa", b: "bb"} {a: "cc", b: "dd", c: "ee"}

2、Object.is():判断两个值是否相等,返回布尔值

用途:es5中,对于0的判断不区分正负值,-0 == +0返回true,NaN == NaN返回 返回false;
Object.is()规避了这些问题

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

3、Proxy:代理对象

基本用法

    const person = {
      name: 'www',
      age: '20'
    }
    const personProxy = new Proxy(person, {
      get(target, key) {
        return target[key] ? target[key] : 'default'
      },
      set(target, key, value) {
        target[key] = value % 2 ? value : 99
      }
    })
    console.log(person.xxx) // undefined
    console.log(personProxy.xxx) // default
    console.log(personProxy.age) //20
    personProxy.age = 100
    console.log(personProxy) //{name: "www", age: 99}

这里注意的一点是,这里被拦截的是”personProxy“,而不是”person“
与Object.definedProperty()的比较:
1、相比与Object.definedProperty只能监听get,set行为,proxy监听的行为更多一些,has、deleteProperty ... 等很多
2、对于数组的push,pop等操作,proxy是监听的整个对象的行为,所以通过set方法能够监听到;而definedProperty需要指定该对象的属性名,对于数组来说,就是指定数组的下标,是监听不到数组的push,pop等操作的

    let arr = []
    let arrProperty = new Proxy(arr, {
      set (target, key, value) {
        console.log(target, key, value) //[1] "length" 1
        target[key] = value
        return true
      }
    })
    arrProperty.push(1)
    console.log(arrProperty) //[1]

3、proxy以非侵入的方式,监听了对象的读写;definedProperty需要指定具体需要监听对象的属性名,与上面的数组类似,如果想要监听一个含有多个属性的对象的读写行为,definedProperty需要遍历这个对象的所有属性

4、Reflect: 封装操作对象的统一API

在之前的es5中,操作对象有很多种方式

    const obj = {
      name: '111',
      age: '22'
    }
    // 判断对象某个属性是否存在
    console.log('name' in obj)
    // 删除某个属性
    console.log(delete obj['name'])
    // 获取对象key
    console.log(Object.keys(obj))

对于不同的操作行为,使用的方式却不同,Reflect的目的是使用同一套方式去操作对象

    const obj = {
      name: '111',
      age: '22'
    }
    // 判断对象某个属性是否存在
    console.log(Reflect.has(obj,'name'))
    // 删除某个属性
    console.log(Reflect.deleteProperty(obj, 'name'))
    // 获取对象key
    console.log(Reflect.ownKeys(obj))

5、用于处理异步,解决回调函数的地狱式嵌套问题

四、全新的数据结构和数据类型

1、class 类

es5写法

    function People (name) {
      // 设置实例属性
      this.name = name;
    }
    // 设置实例的共享方法
    People.prototype.sayHi = function () {
      console.log(this.name)
    }
    let p = new People('tom')
    p.sayHi()

使用class更易理解,结构清晰

      class Peopel {
        constructor (name) {
          this.name = name
        }
        say () {
          console.log(this.name)
        }
      }
      const p = new Peopel('tony')
      p.say()

类的继承

      class Peopel {
        constructor (name) {
          this.name = name
        }
        say () {
          console.log(this.name)   //tom,在子类的sayAge中调用
        }
      }
      class Worker extends Peopel {
        constructor (name,age) {
          super(name)
          this.age = age
        }
        sayAge () {
          super.say()
          console.log(this)  // Worker {name: "tom", age: 18}
          console.log(this.age) // 18
        }
      }
      const p = new Worker('tom', 18)
      p.sayAge()

类的继承还是通过原型链的方式实现,具体可以看下babel转换后的代码
super可以作为函数调用,也可以作为对象调用
作为函数调用时,只能在子类的constructor中调用,此时执行父类的构造函数constructor,执行父类构造函数时,this指向的是子类Worker,所以实例p会有name属性(对于super理解的还是不够,后面要单独学习下)

2、set数据结构:可以理解为集合,不重复

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

3、map数据结构

es5中的对象key只能是字符串,map的key可以是任意数据类型, 可以通过get,set,has等操作map

      const m = new Map()
      const tom = {name: '99'}
      m.set(tom, 90)
      console.log(m.get(tom))
      m.forEach((value, key) => {
        console.log(value, key)
      })

4、Symbol新的数据结构,唯一值

用途:防止全局对象中,某个属性名重名,产生冲突;定义私有属性,外部访问不到,且遍历不到

      const s = Symbol('描述')
      console.log(s)  
      const obj = {
        [Symbol('私有属性')]: '11'
      }
      console.log(obj)   //obj对象Symbol('私有属性')这个属性外部访问不到
      console.log(Object.keys(obj)) //[]
      console.log(Object.getOwnPropertySymbols(obj)) //[Symbol(私有属性)]

      const s1 = Symbol.for('111')
      const s2 = Symbol.for('111')
      console.log(s1 === s2) //true

5、for ... of 遍历

es5中,使用for ... in 遍历键值对结构数据,使用forEach遍历数组
es6中新增了set,map数据结构,for...of是用来统一遍历拥有某一种特性的数据结构(可迭代)

      const arr = [1,2,3] 
      for (const item of arr) {
        // 遍历数组
        console.log(item)
      }
      const s = new Set()
      s.add(1).add(2).add(3)
      for (const item of s) {
        // 遍历set结构
        console.log(item)
      }
      const m = new Map([
        ['name','昵称'],
        ['title', '标题']
      ])
      for (const [key, value] of m) {
        // 遍历map结构
        console.log(key)
        console.log(value)
        console.log(m.get(key))
      }

      const newSet = new Set([
        ['name','昵称'],
        ['title', '标题']
      ])
      const newMap = new Map(newSet)
      for (const [key, val] of newMap) {
        // 遍历set初始化后的map结构
        console.log(key)
        console.log(val)
      }

      const obj = {
        name: 'ttt',
        age: '19'
      }
      for (const [key, val] of obj) {
        // 遍历对象报错 Uncaught TypeError: obj is not iterable
        console.log(key, val)
      }

上面代码中,for...of可以遍历数组,set, map,但是却不能遍历对象,是因为对象没有可迭代接口

6、可迭代接口

在浏览器中打印一个数组,在数组的原型对象上有一个Symbol内置属性Symbol.iterator方法,该方法会返回一个iterator对象,该对象包含一个next()方法,调用iterator.next()会返回一个迭代器结果对象iterationResult,iterationResult对象包含两个值,value为遍历的item,done为当前数据是否遍历完成

      const iterator = arr[Symbol.iterator]()
      console.log(iterator) // Array Iterator {}
      const ite = iterator.next()
      console.log(ite) //{value: 1, done: false} value为迭代器的值,done标识是否遍历完

由于上面代码中obj对象不没有Symbol.iterator的内置方法,所以它不是一个可迭代对象,当使用for...of遍历时就报错了,下面手动实现obj可迭代

      let iteratorObj = {
        //iteratorObj是可迭代对象 iterable
        name: 'ttww',
        age: '18',
        [Symbol.iterator] () {
          let index = 0;
          let arr = []
          for (const key in iteratorObj) {
            arr.push([key, iteratorObj[key]])
          }
          return {
          //返回的对象叫iterator
            next () {
              return {
                //结果对象iterationResult
                value: arr[index],
                done: index++ >= arr.length
              }
            },
          }
        }
      }
      for (const [key, val] of iteratorObj) {
        console.log(key, val)
      }

上面代码中,iteratorObj有了可迭代接口,认为是可迭代对象iterable;Symbol.iterator方法返回的对象是迭代器对象iterator;迭代器对象next方法返回的对象是迭代器结果对象iterationResult

6、生成器 generator

用途:处理异步调用回调嵌套的问题

      function *geFn () {
        console.log('111')
        yield 100
        console.log('222')
        yield 200
        console.log('333')
        yield 300
      }
      let generator = geFn()
     
      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}

在函数名前面加一个"*",函数就变为生成器函数,执行该函数时,里面的函数不会立即执行,而是会返回一个生成器对象,调用生成器对象的.next方法函数开始执行,当遇到”yield“关键字,函数会停止执行,并把yield的值当做next方法返回对象的value; 当下次调用next方法时,函数从当前位置开始继续执行
生成器函数执行后返回的生成器对象generator,内部也是实现了迭代器接口,所以可以使用for...of来遍历

      function *geFn () {
        console.log('111')
        yield 100
        console.log('222')
        yield 200
        console.log('333')
        yield 300
      }
      let generator = geFn()
    
      for (const item of generator) {
        console.log(item)
      }

可以使用生成器函数改写上面对象的迭代器方法

      let iteratorObj = {
        name: 'ttww',
        age: '18',
        [Symbol.iterator]:function *() {
          let index = 0;
          let arr = []
          for (const key in iteratorObj) {
            arr.push([key, iteratorObj[key]])
          }
          // for (let i =0; i<arr.length; i++) {
          //   console.log(arr[i])
          //   yield arr[i]
          // }
         
          for (const item of arr) {
            yield item
          }
          // arr = ['b',3,4,5]
          // return {
          //   next () {
          //     return {
          //       value: arr[index],
          //       done: index++ >= arr.length
          //     }
          //   },
          // }
        }
      }
      for (const [key, val] of iteratorObj) {
        console.log(key, val)
      }

这里有一个注意的点就是在循环arr数组时,不能是有forEach遍历,是因为forEach里需要传一个回调函数,这个函数不是生成器函数,在非生成器函数里使用yield关键字会报错

ES2016

1、数组新增方法:includes

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

2、指数运算符

console.log(2 ** 10) //1024

ES2017

1、Object.values(),以数组的形式,返回对象所有的值

      let obj = {
        title: 'wwwww',
        age: 199
      }
      console.log(Object.values(obj)) //["wwwww", 199]

2、Object.entries(),以数组的形式,返回对象的所有键值对

let obj = {
        title: 'wwwww',
        age: 199
      }
console.log(Object.entries(obj)) //[["title", "wwwww"],["age", 199]]

可以将对象转为map数据结构

      let obj = {
        title: 'wwwww',
        age: 199
      }
     
      let m = new Map(Object.entries(obj))
      console.log(m) //{"title" => "wwwww", "age" => 199}

3、Object.getOwnPropertyDescriptor()
获取一个对象的完整描述信息,可用于将一个对象的get,set属性赋值给另一个对象

      let obj = {
        firstName: 'abc',
        lastName: '123',
        get fullName () {
          return this.firstName + this.lastName
        }
      }
      console.log(obj.fullName) //abc123
      const p2 = Object.assign({}, obj)
      // 此时赋值给p2的仅仅是fullName属性的值,并没有fullName属性的getter
      p2.firstName = '456'
      console.log(p2.fullName)  //abc123

      const description = Object.getOwnPropertyDescriptors(obj)
      console.log(description)
      const p3 = Object.defineProperties({}, description);
      p3.firstName = '789'
      console.log(p3.fullName)  //789123

4、字符串的padEnd()、padStart()方法
5、async/await

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342