浅谈ECMAScript6

1,195 阅读6分钟

对es6语法的练习与总结篇

1.Proxy

Proxy 可以理解成,在目标对象之前架设一层"拦截",外界对该对象访问的时候

都必须先通过这一层拦截,相当于提供了一种机制,可以对外界的访问进行过滤和改写

var proxy = new Proxy(target, handler);

Proxy对象的所有用法,都是上面这种形式,不同的只是 handler 参数的写法。其中, new Proxy() 表示

生成一个Proxy实例,target参数表示所要拦截的目标对象, handler 参数也是一个对象,用来定制拦截行为。

  • get方法拦截读取,里面有两个参数,传入的对象与属性
  • set方法可以设置属性值,但是不会改变原有对象,拦截对象属性的赋值操作
  • has方法可以拦截判断该对象中是否有该属性
  • 拦截delete操作 拦截对象自身属性的读取操作

2.Reflect

程序在运行的时候去获取对象内部的结构就叫做反射

1.将原先Object对象上定义的一些方法,放入Reflect对象(代码重构)  

image.png    // Reflect.defineProperty

2.修改了某些Object方法的返回结果

image.png 3.让Object操作变成函数行为

  •    name in obj/delete obj.name 命令式
  •    Reflect.has(obj.name)/Reflect.deleteProperty(obj,name)

image.png

 4.Reflect对象的方法和Proxy对象方法对应

   //可以让Proxy方便的调用对应的Reflect方法

5.修改指定函数的this指向

image.png

image.png

求数组的最大值最小值

image.png

image.png

image.png 3.观察者模式

在监听对象属性改变的同时,执行指定其他的业务操作

 //先定义一个容器,存放需要执行的业务操作let box = new Set()

​    //定义一个函数将所有的业务操作都放进去let actions = fn => {

​      box.add(fn)

​    }

​    // 定义两个业务let fn1 = () => {

​      console.log("业务一")

​    }

​    actions(fn1)//传入参数,目的是将业务放入容器中let fn2 = () => {

​      console.log("业务二")

​    }

​    actions(fn2)//传入参数,目的是将业务放入容器中//创建一个函数用于生成一个代理对象let createproxy = target => new Proxy(target, {

​      set(target, key, value) {

​        Reflect.set(target, key, value)//设置值//执行其他业务操作

​        box.forEach(fn => {

​          fn()//遍历放在容器中的业务操作,并执行

​        })

​      }

​    })

​    //定义一个实例对象let obj = {

​      name: "wangsu",

​      age: 23,

​      grade: 90,

​      __job__: "IT"

​    }

​    //修改属性,调用函数执行let proxy = createproxy(obj)

​    proxy.name = "kobe"

4.promise

4.1.promise的定义(面试重点)

image.png

Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区

最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

所谓 Promise ,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结

果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操

作都可以用同样的方法进行处理。

    ## 4.2 Promise两个特点

1. 对象的状态不受外界影响。 Promise 对象代表一个异步操作,有三种状态: Pending (进行 中)、 Resolved (已完成,又称Fulfilled)和 Rejected (已失败)。只有异步操作的结果,可以决定当

前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就

是“承诺”,表示其他手段无法改变。

2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果.

 Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变
 为 Rejected 。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
 就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事
 件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
 了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调
 函数。此外, Promise 对象提供统一的接口,使得控制异步操作更加容易。

3. Promise 也有一些缺点。

- 首先,无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。
- 其次,如果不设置回调函数, Promise 内部抛出的错误,不会反应到外部。
- 第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
- 如果某些事件不断地反复发生,一般来说,使用stream模式是比部署 Promise 更好的选择。

4.3.promise解决文件读取顺序和回调地狱问题

let fs = require("fs")
//封装一个读文件的方法
function reads(path) {
  return new Promise((resolve, reject) => {//返回一个promise
​    fs.readFile(path, (err, data) => {//读取文件数据if (!err) {
​        resolve(data.toString())//将读取的数据用resolve返回出去
​      }
​      else {
​        reject(err)//发送拒绝后抛错
​      }

​    })

  })

}

reads("../data/a.txt")
  .then((data) => {//这里函数中的data便是resolve 中返回出来已经读取的数据console.log(data)
​    return reads("../data/b.txt")
​    //这里返回下一个需要读取的文件,在调用一下读取函数,这个函数的返回值是一个Promise

  },
​    (err) => {
​      throw err
​    } ).then((data) => {
​    //由于封装的reads方法返回的是一个promise对象,因此可以继续调用它的then方法console.log(data)
​    return reads("../data/c.txt")
  },(err) => {
​      throw err
​    }).then((data) => {
​      console.log(data)
​      return reads("../data/d.txt")
​    },
​      (err) => {
​        throw err
​      }
​    ).then((data) => {
​      console.log(data)
​    },
​      (err) => {
​        throw err
​      }
​    )

4.4.promise异步解决

image.png

由于开始的时候settimeout是一种异步处理函数,那么当程序执行时,程序不会从上到下依次
执行,遇到异步处理时不会等待,此时会造成settimeoutl里面的代码执行可能会在其他代码
执行之后

4.5.promise-all

image.png

4.6.promise-race

加载请求速度最快的那个请求

image.png

4.7.模拟promise图片加载

//创建一个图片加载函数function loadImage(url,resolve,reject) {
​      let img = new Image()
​      img.src = url
​      img.onload = function () {
​        console.log("图片加载成功")
​        resolve({width:img.width,height:img.height})//在函数中传入参数(图片
的高度宽度)

​      }

​      img.onerror = function () {
​        console.log("图片加载失败")
​        reject()
​      }
​    }

​    let url="https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?
image&quality=100&size=b4000_4000&sec=1594954864&di=c86ca29e92ae5ba6ef0400ba
dc17a69a&src=http://a3.att.hudong.com/14/75/01300000164186121366756803686.jpg"//创建两个函数,分别是图片加载完成和图片加载失败function resolve(data) {
​      console.log("图片加载完成,执行某个处理")
​      console.log(data)
​    }

​    function reject() {

​      console.log("图片加载失败,执行某个处理")
​    }
​    loadImage(url,resolve,reject)

5.defineproperty数据劫持

在给对象赋值的时候可以得到对象赋值的过程

// 这是将要被劫持的对象
const data = {
  name: '',
};

function say(name) {
  if (name === '古天乐') {
    console.log('给大家推荐一款超好玩的游戏');
  } else if (name === '渣渣辉') {
    console.log('戏我演过很多,可游戏我只玩贪玩懒月');
  } else {
    console.log('来做我的兄弟');
  }
}

// 遍历对象,对其属性值进行劫持
Object.keys(data).forEach(function(key) {
  Object.defineProperty(data, key, {
    enumerable: true,
    configurable: true,
    get: function() {
      console.log('get');
    },
    set: function(newVal) {
      // 当属性值发生变化时我们可以进行额外操作
      console.log(`大家好,我系${newVal}`);
      say(newVal);
    },
  });
});

data.name = '渣渣辉';
//大家好,我系渣渣辉
//戏我演过很多,可游戏我只玩贪玩懒月

6.字符串匹配方法

image.png

image.png

7.symbol

7.1.基本意义

image.png

7.2.symbol作为属性调用

image.png

只能用上面的方式

8.Set与Map

8.1.set

8.1.1.常用方法

一种类似数组的数据结构

image.png

8.1.2.数组去重

image.png

image.png

8.1.3.并集,差集,交集

image.png

8.2.WeakSet

8.2.1.与set的区别

eakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值。

const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set

上面代码试图向 WeakSet 添加一个数值和Symbol值,结果报错,因为 WeakSet 只能放置对象。

8.2.2.基本概念(面试问到)

其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内
存,不考虑该对象还存在于 WeakSet 之中。

这是因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为,垃圾回收机制就不会释放
这块内存。结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内
存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。因此,
WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它
在 WeakSet 里面的引用就会自动消失。

由于上面这个特点,WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于
WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不
一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历

8.3.map

JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

const data = {};
const element = document.getElementById('myDiv');

data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"

上面代码原意是将一个 DOM 节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动转为字符串[object HTMLDivElement]。

为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

上面代码在新建 Map 实例时,就指定了两个键name和title

常用方法

(1)size 属性

size属性返回 Map 结构的成员总数。

const map = new Map();
map.set('foo', true);
map.set('bar', false);

map.size // 2

(2)Map.prototype.set(key, value)

set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

const m = new Map();
m.set('edition', 6)        // 键是字符串
m.set(262, 'standard')     // 键是数值
m.set(undefined, 'nah')    // 键是 undefined

set方法返回的是当前的Map对象,因此可以采用链式写法。

let map = new Map()
  .set(1, 'a')
  .set(2, 'b')
  .set(3, 'c');

(3)Map.prototype.get(key)

get方法读取key对应的键值,如果找不到key,返回undefined。

const m = new Map();
const hello = function() {console.log('hello');};
m.set(hello, 'Hello ES6!') // 键是函数
m.get(hello)  // Hello ES6!

(4)Map.prototype.has(key)

has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。

const m = new Map();
m.set('edition', 6);
m.set(262, 'standard');
m.set(undefined, 'nah');
m.has('edition')     // true
m.has('years')       // false
m.has(262)           // true
m.has(undefined)     // true

(5)Map.prototype.delete(key)

delete方法删除某个键,返回true。如果删除失败,返回false。

const m = new Map();
m.set(undefined, 'nah');
m.has(undefined)     // true
m.delete(undefined)
m.has(undefined)       // false

(6)Map.prototype.clear()

clear方法清除所有成员,没有返回值。

let map = new Map();
map.set('foo', true);
map.set('bar', false);
map.size // 2
map.clear()
map.size // 0

9.itertor遍历器

9.1.基本概念

image.png

image.png

  let target = [1, 2, 34, 6, 8, 45, 31, 2]

​    function myinter(arr) {

​      let index = 0return {

​        next() {

return index < arr.length ? { value: arr[index++], done: false } : { value: undefined, done: true }

​     //设置index,使得每次调用next方法时,index加一,对应数组的索引值

}

​      }

​    }

​    let it = myinter(target)//函数调用// console.log(it.next())// console.log(it.next())// console.log(it.next())// console.log(it.next())for (let i = 0; i <= target.length; i++) {

​      console.log(it.next())//调用封装后的next方法

​    }

9.2.接口部署

对象无法使用for of遍历,因为内部没有遍历器

所有能够用for of遍历的对象,需要在它的Sysbol.itertor属性上部署一个itretor接口

image.png

  • 部署好接口后,对象便可以使用for of遍历

image.png

10.Generator函数

10.1.两个特征

异步解决的方案,是一个状态机,封装了多个状态

  • 1.function后有个*
  • 2.函数体内使用yield定义不同的内部状态

10.2.初步使用

 function* fn() {

​      yield "hello";//第一个状态yield "world";//第二个状态return "endconing";//第三个状态

​    }

​    //调用函数

  let fn1 = fn()//函数不会立即执行,返回一个遍历器对象//第一次调用,Generator函数开始执行,直到遇到第一个 yield 语句为止。 next 
//方法返回一个对象,//它 的 value 属性就是当前 yield 语句的值hello, done 属性的值false,表示
//遍历还没有结束let a1= fn1.next()
​    console.log(a1
​    // 第二次调用,Generator函数从上次 yield 语句停下的地方,一直执行到下一个 
//yield 语句。 next 方法// 返回的对象的 value 属性就是当前 yield 语句的值world, done 属性的值
//false,表示遍历还没有结束。let a2= fn1.next()

​    console.log( a2)



​    // 第三次调用,Generator函数从上次 yield 语句停下的地方,一直执行到 return 语句// (如果没有return语句,就执行到函数结束)。next 方法返回的对象的value属性,// 就是紧跟在 return语句后面的表达式的值(如果没有 return 语句,则 value 属
//性的值为undefined),// done 属性的值true,表示遍历已经结束let a3=fn1.next()

​    console.log( a3)

​    // 第四次调用,此时Generator函数已经运行完毕, next 方法返回对象的 value 属
//性为 undefined,// done 属性为true。以后再调用 next 方法,返回的都是这个值。let a4=fn1.next()

​    console.log( a4)

image.png

10.3.与itertor接口的关系

Generator函数就是itertor遍历器对象生成的函数

image.png

yield与return的区别?

相似之处在于,都能返回紧跟在语句后面的那个表达式的值。区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield表达式。

image.png

11.数组解构赋值惰性求值

image.png

12.class类

12.1.class基本语法

image.png

12.2.静态成员

属于构造函数而不属于实例对象,在类中就是属于类而不属于实例对象。要清晰和方便很多。

image.png

image.png

12.3.类继承

12.3.1.基本语法

Class 可以通过关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

image.png 上面代码定义了一个类,该类通过关键字,继承了类的所有属性和方法。但是由于没有部署任何代码,所以这两个类完全一样,等于复制了一个类。下面,我们在内部加上代码。

image.png 上面代码中,方法和方法之中,都出现了关键字,它在这里表示父类的构造函数,用来新建父类的对象。

子类必须在方法中调用方法,否则新建实例时会报错。这是因为子类自己的对象,必须先通过
父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子
类自己的实例属性和方法。如果不调用方法,子类就得不到对象。

12.3.2super 关键字

super 关键字需要写下子类this之前,相当于借用了父类的构造函数

图下:子类需要继承父类的tostring方法,需要使用super.tostring的方式

image.png

13.新定义的的Object方法

1.Object.setPrototypeOf

image.png

2.Object.keys

image.png

3.Object.values

image.png

4.Object.entries

image.png

5.getOwnPropertyDescriptor

image.png

6.Object.assign()

Object.assign()方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。

const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}

Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。

注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
  • 如果只有一个参数,Object.assign()会直接返回该参数。
const obj = {a: 1};
Object.assign(obj) === obj // true

14.函数的rest参数与扩展运算符

14.1.rest参数

用于获取函数的实参,代替arguments

image.png

14.2.扩展运算符

将数组分割成逗号分隔的参数序列

15.数组中增加的一些方法

15.1.find,findindex,filter的比较

image.png

15.2.value,key,entries的比较

image.png

16.函数参数的解构赋值

image.png

17.箭头函数

image.png 上面代码之中,只有一个,就是函数的,所以都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的,它们的其实都是最外层函数的。

18.链判断运算符

编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取,安全的写法是写成下面这样

image.png

image.png

image.png

19.async 函数

    //1.使用then方法调用//2.使用await//只能在async函数中使用,await之后只能是一个promise对象

19.2具体使用

//request.js封装请求
let baseUrl="http://192.168.24.36:3000/"

function http(url){
  return $.ajax({
​    url:baseUrl+url,
​    type:"get"
  })

}
//index.html页面中发送请求
let url="top/playList?cat=华语"async function getdata(){
​      let cat=await http(url)
​      let id=cat.playlists[0].idlet detail=await http(`playlist/detail?id=${id}`)
​      let songId=detail.playlist.tracks[0].idlet songurl=await http(`song/url?id=${songId}`)
​      console.log(songurl)
​    }

​    getdata()

20总结

这是一篇根据对学习es6的总结,对学习es6的记录,总结供大家参考讨论

21.参考文献

  • ECMAScript6 (原著:阮一峰)
  • 深入理解ES6