ES6的学习(三)

Promise

  • Promise 是异步编程的一种解决方案,Promise对象是一个构造函数,用来生成Promise实例。

    const promise = new Promise(function(resolve, reject) {
      // ... some code
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
  • Promise构造函数接受一个函数作为参数,该函数两个参数分别是resolve和reject,他们是两个函数

    • resolve函数作用:把Promise对象的状态从未完成变成成功(pending=>resolved)
    • reject函数作用:把promise对象的状态从未完成变成失败(pending=>rejected)
  • Promise.prototype.then

    • then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。
    • then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
      getJSON("/posts.json").then(function(json) {
        return json.post;
      }).then(function(post) {
        // ...
      });
      
  • Promise.prototype.catch()

    • Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
    • 如果Promise实例对象抛出错误的话,也会走到catch方法的回调。如果.then()的处理出现异常也会走到catch回调方法中
  • Promise.prototype.finallyfinally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

    Promise.prototype.finally = function (callback) {
      let P = this.constructor;
      return this.then(
        value  => P.resolve(callback()).then(() => value),
        reason => P.resolve(callback()).then(() => { throw reason })
      );
    };
    
  • Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

    • Promise.all()方法接受一个具有 Iterator 接口的对象作为参数
    const p = Promise.all([p1, p2, p3]);
    p的状态由p1、p2、p3决定,分成两种情况。
    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
    
  • Promise.race() 方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.race([p1, p2, p3]);
    上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
    
    Promise.race()方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。
    
    下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为reject,否则变为resolve。
    
  • Promise.allSettled() 用来确定一组异步操作是否都结束了(不管成功或失败)。

    // Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
    const promises = [
      fetch('/api-1'),
      fetch('/api-2'),
      fetch('/api-3'),
    ];
    
    await Promise.allSettled(promises);
    removeLoadingIndicator();
    // 该方法返回的新的 Promise 实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。
    
  • Promise.any() 只要有一个变成fulfilled,包装实例就会变成fulfilled状态或者所有如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

  • Promise.resolve()将现有对象转为 Promise 对象, Promise.resolve()方法就起到这个作用。分为四种情况
    (1). 参数是一个 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
    (2). 参数是一个thenable对象,(thenable对象指的是具有then方法的对象)

    // Promise.resolve()方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法。
    let thenable = {
      then: function(resolve, reject) {
        resolve(42);
      }
    };
    let p1 = Promise.resolve(thenable);
    p1.then(function (value) {
      console.log(value);  // 42
    });
    

    (3). 参数不是具有then()方法的对象,或根本就不是对象

    // 如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。
    const p = Promise.resolve('Hello');
    
    p.then(function (s) {
      console.log(s)
    });
    // Hello
    

    (4). 不带有任何参数

    // Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
    	const p = Promise.resolve();
    
    p.then(function () {
      // ...
    });
    
  • Promise.reject() Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

    const p = Promise.reject('出错了');
    // 等同于
    const p = new Promise((resolve, reject) => reject('出错了'))
    
    p.then(null, function (s) {
      console.log(s)
    });
    // 出错了
    

Iterator 和 for…of 循环

  • 为了使不同数据结构的数据能统一操作,提供了遍历器(Iterator)就是这样一种机制。
  • 它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
  • Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。
  • 原生具备Iterator 接口的数据(可用for of遍历)有:Array,arguments,set,map,string,typedArray,nodeList
  • Iterator 的遍历过程是这样的。
    1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
    
    (2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。
    
    (3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。
    
    (4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。
    
    每一次调用next方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。
    
  • 对象(Object)之所以没有默认部署 Iterator 接口,是因为对象的哪个属性先遍历,哪个属性后遍历是不确定的,需要开发者手动指定。
    //声明一个对象
    // object是不具有Iterator 接口的。但是我们可以手动添加一个Iterator 接口
    // 下面的方法就是给obj添加一个Iterator 接口。让obj通过for of遍历 时,遍历的只是里面stus数组的数据
    const obj = {
    	name:"name",
    	stus:["zhangsan","lisi","wangwu"],
    	[Symbol.iterator](){
    		let index = 0;
    		let _this = this;
    		return{
    			next:function(){
    				if(index<_this.stus.length){
    					const result = {value:_this.stus[index],done:false};
    					index++
    					return result
    				}
    				else {
    					return {value:undefined,done:true}
    				}
    			}
    		}
    	}
    }
    
  • 调用 Iterator 接口的场合
    1. 解构赋值,对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。
    2. 扩展运算符,也会调用默认的 Iterator 接口。
    3. yield*
      let generator = function* () {
        yield 1;
        yield* [2,3,4];
        yield 5;
      };
      
      var iterator = generator();
      
      iterator.next() // { value: 1, done: false }
      iterator.next() // { value: 2, done: false }
      iterator.next() // { value: 3, done: false }
      iterator.next() // { value: 4, done: false }
      iterator.next() // { value: 5, done: false }
      iterator.next() // { value: undefined, done: true }
      // next方法也是可以传参数的。next传递的参数时作为上一次yield 的返回结果
      let generator = function* () {
        let one = yield 1;
        // 这个one就是第二次next('参数')的传递的参数
        yield* [2,3,4];
        yield 5;
      };
      var iterator = generator();
      iterator.next() // { value: 1, done: false }
      iterator.next(2) // { value: 2, done: false }  //上面的one就是next中的参数2
      
    4. 其他场合
      for...of
      Array.from()
      Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
      Promise.all()
      Promise.race()
      
  • for of 与其他遍历语法的比较已数组为例
    • for循环(这种写法比较麻烦)
    for (var index = 0; index < myArray.length; index++) {
      console.log(myArray[index]);
    }
    
    • forEach 无法中途跳出forEach循环,break命令或return命令都不能奏效
    myArray.forEach(function (value) {
      console.log(value);
    });
    
    • for in
    for (var index in myArray) {
      console.log(myArray[index]);
    }
    // 遍历得到的是索引
    // 还会遍历手动添加的其他键,甚至包括原型链上的键。
    // for...in循环会以任意顺序遍历键名。(不能保证顺序)
    
    • for of 没有for in的缺点,且可以与breakcontinuereturn配合使用。

Generator 函数的语法

Generator 函数是 ES6 提供的一种异步编程解决方案

  • 它的具体执行步骤如下:
    1. 执行 Generator 函数会返回一个遍历器对象(这个遍历器对象就是Iterator)可以依次遍历 Generator 函数内部的每一个状态。
    2. 得到这个遍历器对象后,下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。
    3. Generator 函数配合内部的yield语法使用
    4. yield表达式是暂停执行的标记,当使用next方法Ⅹ就是恢复执行

yield

  • 由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

  • 遍历器对象的next方法的运行逻辑如下。
    1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

    (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。

    (3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

    (4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

    需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。

与 Iterator 接口的关系

  • 任意一个对象的Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。
  • 由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。
    var myIterable = {};
    myIterable[Symbol.iterator] = function* () {
      yield 1;
      yield 2;
      yield 3;
    };
    
    [...myIterable] // [1, 2, 3]
    

next()方法的参数

  • yield表达式本身没有返回值,或者说总是返回undefinednext方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

for…of

  • for…of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法。
    function* foo() {
      yield 1;
      yield 2;
      yield 3;
      yield 4;
      yield 5;
      return 6;
    }
    
    for (let v of foo()) {
      console.log(v);
    }
    // 1 2 3 4 5
    // 上面代码当执行到return 6的时候,next()的返回值为{value:6,done:true},此时for..of会中止.且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of循环之中。
    
  • Generator.prototype.return
    Generator 函数返回的遍历器对象,还有一个return()方法,可以返回给定的值,并且终结遍历 Generator 函数。
    function* gen() {
      yield 1;
      yield 2;
      yield 3;
    }
    
    var g = gen();
    
    g.next()        // { value: 1, done: false }
    g.return('foo') // { value: "foo", done: true }
    g.next()        // { value: undefined, done: true }
    
  • Generator的一个使用
    // 有三个异步操作。1秒后输出111,2秒后输入222 3秒后输入333.每一个都要在上一个执行后才执行下一个操作
    // 通过回调地狱的方式写代码的话会有层层嵌套问题。可以使用Generator函数
    function one(){
    	setTimeout(()=>{
    	console.log(111)
    	iterator.next()
    	},1000)
    }
    function two(){
    	setTimeout(()=>{
    	console.log(222)
    	iterator.next()
    	},2000)
    }
    function three(){
    	setTimeout(()=>{
    	console.log(333)
    	},3000)
    }
    function * gen(){
    	yield one()
    	yield two()
    	yield three()
    }
    // 调用
    let iterator = gen()
    
    // 传递参数的iterator 函数
     function getUser(){
     	setTimeout(()=>{
    		let data = '用户数据'
    		// next中的参数就是上一次yield的返回值
    		iterator.next(data)
    	},3000)
     }
     function getOrder(user){
     	console.log(user)
     	setTimeout(()=>{
    		let data = '订单数据'
    		iterator.next(data)
    	},3000)
     }
     function getGood(order){
     	console.log(order)
     	setTimeout(()=>{
    		let data = '商品数据'
    	},3000)
     }
     function * gen(){
    	const user = yield getUser()
    	const order= yield getOrder(user)
    	const good= yield getGood(order)
    }
    let iterator = gen()
    

async await

  • 含义: async 函数是什么?一句话,它就是 Generator 函数的语法糖。
  • 语法:
    const asyncReadFile = async function () {
      const f1 = await readFile('/etc/fstab');
      const f2 = await readFile('/etc/shells');
      console.log(f1.toString());
      console.log(f2.toString());
    };
    
  • async函数对 Generator 函数的改进,体现在以下四点。
    1. 内置执行器。async函数的执行,与普通函数一模一样,只要一行。这完全不像 Generator 函数,需要调用next方法,或者用co模块,才能真正执行,得到最后结果。
    2. 更好的语义。
    3. 更广的适用性.yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即resolvedPromise 对象)。
    4. 返回值是 PromiseGenerator 函数的返回值是 Iterator 对象
  • async 函数的实现原理(async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。)

class的基本语法

  • ES6引入了 Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。
class Point {
  constructor(x, y) {
  //this关键字则代表实例对象
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}
  • constructor()默认返回实例对象(即this)
  • 通过class创建实例对象的时候必须new
  • class中的getter和setter取值函数(getter)和存值函数(setter)
    class MyClass {
      constructor() {
        // ...
      }
      get prop() {
        return 'getter';
      }
      set prop(value) {
        console.log('setter: '+value);
      }
    }
    
    let inst = new MyClass();
    
    inst.prop = 123;
    // setter: 123
    
    inst.prop
    // 'getter'
    
  • 通过 static 注册静态方法以及静态属性
    class Foo {
      static classMethod() {
      	// this.aaa = 'xxx'
      	// 如果静态方法里面使用this,那这个this指的是类而不是实例
        return 'hello';
      }
      static name = 'xxx'
    }
    // 静态方法以及静态属性通过类调用,而不是实例
    
  • 通过#注册私有方法以及属性(提案), 私有属性只能在类中使用,在外部使用就会报错
    class IncreasingCounter {
      #count = 0;
      get value() {
        console.log('Getting the current value!');
        return this.#count;
      }
      increment() {
        this.#count++;
      }
    }
    
  • 静态块(ES2022新增了静态块这一提案)
    // 静态块内部可以使用this替代当前类
    class C {
      static x = ...;
      static y;
      static z;
    
      static {
        try {
          const obj = doSomethingWith(this.x);
          this.y = obj.y;
          this.z = obj.z;
        }
        catch {
          this.y = ...;
          this.z = ...;
        }
      }
    }
    

class的继承

  • class可以通过extend关键字实现继承,让子类继承父类的属性和方法(es5通过原型链继承)
    	class ColorPoint extends Point {
    }
    
  • 子类必须在constructor函数中调用super()
    class Point { /* ... */ }
    // super作用就是使用通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,添加子类自己的实例属性和方法
    // super执行之前使用this会报错,因为子类实例的构建,必须先完成父类的继承,只有super()方法才能让子类实例继承父类。
    class ColorPoint extends Point {
      constructor(x, y, color) {
        super(x, y); // 调用父类的constructor(x, y)
        this.color = color;
      }
    
      toString() {
        return this.color + ' ' + super.toString(); // 调用父类的toString()
      }
    }
    
  • Object.getPrototypeOf()用来从子类上获取父类
  • super关键字
    • super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。(作为函数时只能用在子类的构造函数中)
    class A {}
    
    class B extends A {
      constructor() {
        super();
      }
    }
    super虽然代表了父类A的构造函数,但是返回的是子类B的实例,即super内部的this指的是B的实例,因此super()在这里相当于A.prototype.constructor.call(this)
    • 第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
    class A {
      p() {
        return 2;
      }
    }
    
    class B extends A {
      constructor() {
        super();
        // 这里只能通过super调用父类上的静态属性以及方法,不是使用父类实例上的方法以及属性
        console.log(super.p()); // 2
      }
    }
    
    let b = new B();
    
  • 类的 prototype 属性和__proto__属性
    • 子类的__proto__属性,表示构造函数的继承,总是指向父类。
    • 子类prototype属性的__proto__属性,表示方法的继承,总是指向父类的prototype属性。
      class A {
      }
      
      class B extends A {
      }
      
      B.__proto__ === A // true
      B.prototype.__proto__ === A.prototype // true
      
  • 实例的 proto 属性
    • 子类实例的__proto__属性的__proto__属性,指向父类实例的__proto__属性。也就是说,子类的原型的原型,是父类的原型。
      var p1 = new Point(2, 3);
      var p2 = new ColorPoint(2, 3, 'red');
      
      p2.__proto__ === p1.__proto__ // false
      p2.__proto__.__proto__ === p1.__proto__ // true
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值