ES6(2)

153 阅读9分钟

迭代器Iterator

    //Iterator
    //是一种新的遍历机制
    /*1.迭代器是一个接口,能快捷的访问数据,通过Symbol.iterator创建迭代器
    通过迭代的next方法获得迭代之后的结果*/
    //2.迭代器适用于遍历数据结构的指针
    
    //使用迭代
    const items = ['one', 'two', 'three'];
    //1.创建新的迭代器
    const ite = items[Symbol.iterator]();
    console.log(ite.next());
    //{done: false value: "one"} done如果为false表示遍历继续 如果为true表示遍历完成
    console.log(ite.next());
    console.log(ite.next());
    console.log(ite.next()); //{done: true value: undefined}

生成器Generator

    function* add() {
      console.log('start');
      /* x 不是yield'2'的返回值,它是next()调用
      恢复当前yield()执行传入的实参*/
      let x = yield '2';
      console.log('one:' + x);
      let y = yield '3';
      console.log('two:' + y);
      return x + y;
    }
    const fn = add();
    console.log(fn.next());//start {value: "2", done: false}
    console.log(fn.next(10));
    //one:10 {value: "3", done: false}
    console.log(fn.next(20));//two:20 {value: 30, done: true}

使用场景:为不具备Inetrator接口的对象提供了便利操作

    function* objEntries(obj) {
      //获取对象的所有的key保存到数组[name,age]
      const propKeys = Object.keys(obj);//必须要有keys()操作
      for (const propkey of propKeys) {
        yield [propkey, obj[propkey]];
      }
    }

    const obj = {
      name: 'qaq',
      age: 20
    }
    obj[Symbol.iterator] = objEntries;

    for (let [key, value] of objEntries(obj)) {
      console.log(`${key}:${value}`);//name:qaq age:20
    }

解决页面加载的异步问题(让异步代码同步化)

    function* load(name) {
      let a = yield;
      loadUI();
      yield showData(a);
      hideUI();
    }

    let load1 = load();
    load1.next();
    load1.next(load1);

    function loadUI() {
      console.log('加载loading...页面');
    }

    function showData(name) {
      //模拟异步操作
      setTimeout(() => {
        console.log('数据加载完成');
        name.next();
      }, 1000);
    }

    function hideUI() {
      console.log('隐藏loading...页面');
    }

打印结果为

  1. 加载loading...页面
  2. 数据加载完成
  3. 隐藏loading...页面

如果不使用该操作由于有setTimeout,hideUI()会比showData()更先加载出来

Promise

    //相当于一个容器,保存着未来才会结束的事件(异步操作)的一个结果
    //各种异步操作都可以用同样的操作进行处理

    //特点:
    //1.对象的状态不受外界影响 
    //处理异步操作三个状态 Pending(进行中)Resolved(成功) Rejected(失败)
    //2.一旦状态改变,就不会再变,任何时候都可以得到这个结果

    let pro = new Promise(function (resolved, rejected) {
      //执行异步操作
      let person = {
        code: 201,
        data: {
          name: 'qaq'
        },
        error: '验证错误'
      }
      setTimeout(() => {
        if (person.code === 200) {
          resolved(person.data)
        } else {
          rejected(person.error)
        }
      }, 1000)
    })
    console.log(pro);
    pro.then((val) => {
      console.log(val);
    }, (err) => {
      console.log(err);
    })
    

改变延迟时间(封装)

    function timeOut(ms) {
      return new Promise((resolved, rejected) => {
        setTimeout(() => {
          resolved('hello');
        }, ms);
      })
    }

    timeOut(2000).then((val) => {
      console.log(val);
    })

Promise方法

1. then()有两个参数第一个为resolved回调函数,第二个参数是可选的 为rejected状态的回调函数,then()返回的是一个新的promise实例,可以采用链式编程

2. catch()只有rejected状态的回调函数

3. resolve() 能将现有的任何对象转换成promise对象

    //resolve()
    let p = Promise.resolve('foo');
    console.log(p);//Promise {<resolved>: "foo"}
    p.then((val) => {
      console.log(val);//foo
    })
let p = Promise.resolve('foo');

等同于

let p = new Promise(resolve => {
    return resolve('foo')
});

4. all()

应用:一些游戏类的素材比较多,等待图片、flash、静态资源都加载完成,才进行页面的初始化

    //all()
    let promise1 = new Promise((resolve,reject) => {});
    let promise2 = new Promise((resolve,reject) => {});
    let promise3 = new Promise((resolve,reject) => {});

    let p4 = Promise.all([promise1,promise2,promise3]);
    p4.then(() => {
      //三个都成功才成功
    }).catch(err =>{
      //如果有一个失败 则失败
    })

5. race()超时

race([要进行的操作方法,超时的方法]).then(如果在规定时间内完成操作返回这部分代码).catch(如果没在规定时间内完成操作返回这部分代码))

//race() 某个异步请求设置超时时间,并且在超时后执行相应的操作
    //1.请求图片资源
    function requestImg(imgSrc) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = function () {
          resolve(img);
        }
        img.src = imgSrc;
      });
    }
    //2.设置超时条件及内容
    function timeout() {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          reject(new Error('图片请求超时'));
        }, 3000);
      })
    }
    //3.实现超时操作 
    Promise.race([requestImg(
        'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3252521864,872614242&fm=26&gp=0.jpg'), timeout()])
      .then(val => {
        console.log(val);
        document.body.appendChild(val);
      }).catch(err => {
        console.log(err);
      })

6.done() finally()

无论加载状态成功还是失败都会运行

async

then()执行await语句 catch()执行throw语句

    //作用:使异步操作更加方便
    //基本操作 async它会返回一个Promise对象 then catch
    //asynv是Generator的一个语法糖
    async function f() {
      //return await ('hello async');
      let a = await 'hello async';
      let data = await a.split('');
      return data;
    }
 
    //如果async函数中有多个await,那么then函数会等待所有的await指令运行完的结果才会执行
    f().then((val) => {
      console.log(val); //(11) ["h", "e", "l", "l", "o", " ", "a", "s", "y", "n", "c"]
    }).catch(e => {
      console.log(e);
    })

如果一旦运行了 await rejected 后面就不会再继续运行了

    async function f2() {
      throw new Error('出错了');
    }
    f2().then(v => console.log(v)).catch(e => console.log(e));
    //Error: 出错了

    async function f3() {
      try {
        await Promise.reject('出错了');
      } catch (error) {

      }
      return await Promise.resolve('hello');
    }
    f3().then(v => console.log(v)).catch(e => console.log(e));
    //hello

Class类

    //es6 造类(构造函数)
    class Person {
      //实例化的时候会立即被调用
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }
      sayName() {
        return this.name
      }
      sayAge() {
        return this.age
      }
    }
    // 通过Object.assign()方法一次性向类中添加多个方法
    Object.assign(Person.prototype,{
      sayName() {
        return this.name
      },
      sayAge() {
        return this.age
      }
    })
    let p1 = new Person('qaq', 20);
    console.log(p1); //Person {age: 20 name: "qaq"}
    console.log(p1.sayName()); //qaq

class语法

参考文章:cloud.tencent.com/developer/a… (写得非常详尽,强力安利!)

1. constructor

当我们用class定义了一个类,然后用关键字new调用该类,则会自动调用该类中的 constructor函数,最后生成一个实例对象。constructor函数内部的 this 指向的也是新生成的实例对象。

如果要生成一个不需要任何属性的实例对象,则我们不需要在 constructor函数里写任何代码,此时可以省略它,如果没有写constructor,则会自动生成一个空的constructor函数。也正是因为 constructor函数的存在,class 定义的类必须通过 new 来创建实例对象,否则就会报错.

而传统的构造函数就可以不通过new来调用,因为其本身就是一个函数,若不加关键字 new,则相当于直接执行该函数

1. class上的方法为不可枚举属性

Aniaml.prototype.test = function(){}; 可以用object.keys(Animal.prototype)遍历到,因为是可枚举属性。如果在class Animal{ test()=function(){} }; 则用object.keys(Animal.prototype)遍历不到,是因为class定义的方法为不可枚举属性。可以用Object.getOwnPropertyNames(Animal.prototype)可以获取到。

2. static

class类中的方法都是写在原型上的,因此生成的实例对象可以直接调用。现在有一个关键字 static,若写在方法的前面,则表示此方法不会被写在原型上,而只作为该类的一个方法,这样的方法叫做静态方法;相反,若没加关键字 static 的方法就叫做非静态方法

class Person {
   show() {
   	console.log('我是非静态方法show')
   }
   static show() {
   	console.log('我是静态方法show')
   }
   static hide() {
   	console.log('我是静态方法hide')
   }
}

Person.show()    // 我是静态方法show
var person = new Person()
person.show()    // 我是非静态方法show
person.hide()    /*	person.hide()
                         ^
   		  TypeError: person.hide is not a function				
                 */

除了静态方法还有静态属性

    static test = 'test';

如何访问静态属性,在静态方法中用this.test 就能获取到(根据下一条知识点 3. this指向的原理)

3. this指向

class中static方法里的this指向class本身,原型上的方法this指向实例对象

extends(类的继承)

    //class类的继承 extends
    class Animal {
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }
      sayName() {
        return this.name;
      }
      sayAge() {
        return this.age;
      }
    }
    //写个Dog类 继承Animal
    class Dog extends Animal {
      constructor(name, age, color) {
        super(name, age); //super会调用父类函数
        this.color = color;
      }
      //重写父类的方法
      sayName() {
        return this.name + '年龄是' + super.sayAge() + '岁'
      }
    }
    let d1 = new Dog('小黄', 5, '黄色');
    console.log(d1); //Dog {name: "小黄", age: 5, color: "黄色"}
    console.log(d1.sayName());小黄年龄是5

class的继承

1. super

在ES6中规定了,在子类继承了父类以后,必须先在子类的 constructor函数中调用 super函数,其表示的就是父级的 constructor函数,作用就是为子类生成 this对象,将父类的属性和方法赋值到子类的 this上。因此,若没有调用 super函数,则子类无法获取到 this对象,紧接着就会报错.

一: super指向

super()代表的是父类的构造函数,其实 super还可以作为对象使用,即不作为函数调用。当 super在子类的普通方法内时,指向的是父类的原型对象;在子类的静态方法内时,指向的是父类

class A{
	show1() {
		console.log('我是A类的show1方法')
	}
}

class B extends A{
	constructor() {
		super()
	}
	show2() {
		super.show1()
	}	
}

var b = new B()
b.show2()              // 我是A类的show1方法

上述代码,B类继承 A类,其中 A类有一个 show1方法,是写在其原型 A.prototype上的,而在 B类的 show2方法中调用了 super.show1(),我们说过 super在普通的方法中指向的是父类的原型对象,所以 super.show1() 相当于 A.prototype.show1()

二:super在子类静态方法的指向

我们再来看一个super在子类的静态方法中的例子

class A{
    static hide1() {
        console.log('我是A类的hide1方法')
    }
}

class B extends A{
    constructor() {
        super()
    }
    static hide2() {
        super.hide1()
    }
}

B.hide2()                     // 我是A类的hide1方法

上述代码,B类继承 A类,B类在其静态方法 hide2中调用了 super.hide1(),因为 super在静态方法中指向的是父类,所以super.hide1()就相当于 A.hide1()

三:子类普通方法中用super调用了父类的方法,this指向

还需要注意的是,当我们在子类的普通方法中通过 super调用父类的方法时,方法中的 this指向的是当前子类的实例对象

class A {
    constructor() {
        this.name = 'Jack'
    }
    show1() {
        console.log(this.name)
    }
}

class B extends A{
    constructor() {
        super();
        this.name = 'Lpyexplore'
    }
    show2() {
        super.show1()
    }
}

var b = new B()
b.show2()                 // Lpyexplore
四:子类静态方法中用super调用父类方法,this指向

那么,当我们在子类的静态方法中通过super调用父类的方法时,方法中的 this指向的是子类,而不是子类的实例对象

class A {
    constructor() {
        this.x = 1
    }
    static show1() {
        console.log(this.x)
    }
}

class B extends A{
    constructor() {
        super();
        this.x = 2
    }
    static show2() {
        super.show1()
    }
}

B.show2()                 // undefined
B.x = 3
B.show2()                 // 3

ES6模块化

  1. es6模块功能主要由两个命令构成: export、import
  2. export用于规定模块的对外接口 import用于输入其它模块提供的功能
  3. 一个模块就是一个独立的文件
    例子:先创建一个JS文件
export const name = '张三';
export const age = 24;
export function sayName() {
  return 'my name';
}
const obj = {
  foo: 'foo'
}
export default obj;

dafault指的是默认值 所有变量都要经过export对外抛出(除了default)每个文件只有一个default
接下来是html文件

 <script type="module">
    // //es6 module  
    import obj,{name,age,sayName} from './modules/index.js'
    //obj指的是默认值 每个js文件只能抛出一个默认值default
    //等同于下一句
    //import * as f from './modules/index.js'
    console.log(obj);//{foo: "foo"}
    console.log(name,age,sayName());//张三 24 my name

引用的格式: import 默认值(可无),{需要引用的值} from '需要引用的文件路径'

ES6模块与CommonJS模块有什么区别?

1、ES6 Module和CommonJS模块的区别:

CommonJS 是对模块的浅拷贝,ES6 Module 是对模块的引用,即ES6 Module只存只读,不能改变其值,具体点就是指针指向不能变,类似 const import 的接口是 read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对 commonJS 对重新赋值(改变指针指向),但是对 ES6 Module 赋值会编译报错。

2、ES6 Module和CommonJS模块的共同点:

CommonJS和ES6 Module都可以对引入的对象进行赋值,即对对象内部属性的值进行改变。

3、 COMMONJS和ES6的区别

它们有两个重大差异:

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。