• 第五十周总结——JavaScript设计模式


    对该文章的学习总结https://juejin.cn/book/6844733790204461070

    设计模式

    设计模式

    构造器模式

    创建的多个实例对象都具有相同的属性。

    function User(name , age, career) {
      this.name = name
      this.age = age
      this.career = career 
    }
    const user = new User(name, age, career)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    简单工厂模式

    创建的多个实例对象都既有有相同的属性又有不同的属性。

    function User(name , age, career, work) {
        this.name = name
        this.age = age
        this.career = career 
        this.work = work
    }
    
    function Factory(name, age, career) {
        let work
        switch(career) {
            case 'coder':
                work =  ['写代码','写系分', '修Bug'] 
                break
            case 'product manager':
                work = ['订会议室', '写PRD', '催更']
                break
            case 'boss':
                work = ['喝茶', '看报', '见客户']
            case 'xxx':
                // 其它工种的职责分配
                ...
                
        return new User(name, age, career, work)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    抽象工厂模式

    抽象工厂不干活,抽象工厂里面的具体工厂干活。遵守开放封闭原则。让代码对拓展开放,对修改封闭。
    例:我们创建一个手机工厂的流水线。

    1. 定义手机抽象工厂
    class MobilePhoneFactory {
      // 提供操作系统的接口
      createOS(){
          throw new Error("抽象工厂方法不允许直接调用,你需要将我重写!");
      }
      // 提供硬件的接口
      createHardWare(){
          throw new Error("抽象工厂方法不允许直接调用,你需要将我重写!");
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 定义手机具体工厂,具体工厂继承于抽象工厂
    class FakeStarFactory extends MobilePhoneFactory {
      createOS() {
          // 提供安卓系统实例
          return new AndroidOS()
      }
      createHardWare() {
          // 提供高通硬件实例
          return new QualcommHardWare()
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 定义手机操作系统的抽象产品
    class OS {
        controlHardWare() {
            throw new Error('抽象产品方法不允许直接调用,你需要将我重写!');
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 定义手机操作系统的具体产品
    class AndroidOS extends OS {
        controlHardWare() {
            console.log('我会用安卓的方式去操作硬件')
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 定义手机硬件的抽象产品
    class HardWare {
      // 手机硬件的共性方法,这里提取了“根据命令运转”这个共性
      operateByOrder() {
          throw new Error('抽象产品方法不允许直接调用,你需要将我重写!');
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. 定义手机硬件的具体产品
    class MiWare extends HardWare {
      operateByOrder() {
          console.log('我会用小米的方式去运转')
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果我们想再创建一部新的手机,就可以直接重新创建一个类来继承不同的操作系统和硬件。这样就实遵循了开放封闭原则。

    单例模式

    让一个类的实例只存在一个,不管我们尝试去创建多少次,它都只给你返回第一次所创建的那唯一的一个实例。

    1. 使用ES6的class实现单例模式
    class Requset {
      constructor(){}
      static instance;
      static getInstance(){
        if(!this.instance){
          this.instance = new Requset();
        }
        return this.instance;
      }
    }
    
    const r1:Requset = Requset.getInstance();
    const r2:Requset = Requset.getInstance();
    
    console.log(r1 === r2);//true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 使用闭包实现单例模式
    SingleDog.getInstance = (function() {
        // 定义自由变量instance,模拟私有变量
        let instance = null
        return function() {
            // 判断自由变量是否为null
            if(!instance) {
                // 如果为null则new出唯一实例
                instance = new SingleDog()
            }
            return instance
        }
    })()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. Vuex中的单例模式
      Vuex内部实现了一个install方法,这个方法再被调用时会将Store注入到Vue实例中去,Vuex就是使用单例模式让全局Store唯一。
    let Vue // 这个Vue的作用和楼上的instance作用一样
    export function install (_Vue) {
      // 判断传入的Vue实例对象是否已经被install过Vuex插件(是否有了唯一的state)
      if (Vue && _Vue === Vue) {
        if (process.env.NODE_ENV !== 'production') {
          console.error(
            '[vuex] already installed. Vue.use(Vuex) should be called only once.'
          )
        }
        return
      }
      // 若没有,则为这个Vue实例对象install一个唯一的Vuex
      Vue = _Vue
      // 将Vuex的初始化逻辑写进Vue的钩子函数里
      applyMixin(Vue)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    单例模式练习

    1. 实现Storage,使得该对象为单例,基于 localStorage 进行封装。实现方法 setItem(key,value) 和 getItem(key)。
      使用ES6的class实现
    class Storage {
        constructor(){};
        static instance;
        static getInstance(){
            if(!this.instance){
                this.instance = new Storage();
            }
            return Storage.instance;
        };
        getItem(key){
            return localStorage.getItem(key);
        };
        setItem(key, value){
            return localStorage.setItem(key, value);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用闭包实现

    function StorageBase () {}
    StorageBase.prototype.getItem = function (key){
        return localStorage.getItem(key)
    }
    StorageBase.prototype.setItem = function (key, value) {
        return localStorage.setItem(key, value)
    }
    const Storage = (function(){
        let instance = null
        return function(){
            // 判断自由变量是否为null
            if(!instance) {
                // 如果为null则new出唯一实例
                instance = new StorageBase()
            }
            return instance
        }
    })()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    1. 实现一个全局的模态框
    
    
    
        
        单例模式弹框
    
    
    
    	
    	
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    原型模式

    在JavaScript中只存在原型模式,不存在类模式,JavaScript中的类是使用原型继承的语法糖。
    例如:

    class Dog {
        constructor(name, age){
            this.name = name;
            this.age = age;
        }
        eat(){
            console.log('吃骨头');
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    可以将上面的代码写成下面的形式

    function Dog(name, age){
        this.name = name;
        this.age = age;
    }
    Dog.prototype.eat = function(){
        console.log('吃骨头');
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 对象的深拷贝
      注意:深拷贝没有完美的方案,每一种方案都有它的边界
    • 第一种方法使用JSON.stringify和JSON.parse的方式
    const data = {
      name: 'tom',
      fn: ()=>{
        console.log('我是一个函数');
      }
    }
    const dataStr = JSON.stringify(data);
    const copy = JSON.parse(dataStr);
    console.log(copy);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上面的方法只拷贝了name参数,没有拷贝fn参数,应为使用JSON.stringify和JSON.parse的方式进行深拷贝不能对函数和正则进行拷贝。

    • 第二种使用递归的方式
    function deepClone(obj){
      if(typeof obj!=='object'||obj===null){
        return obj;
      }
      let ans={};
      if(obj.constructor===Array){
        ans=[];
      }
      for(let key in obj){
        console.log(obj.hasOwnProperty(key));
        if(obj.hasOwnProperty(key)){
          ans[key] = deepClone(obj[key]);
        }
      }
      return ans;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    装饰器模式

    装饰器模式为了不被已有的业务逻辑干扰,实现新的逻辑
    例:我们现在点击按钮,弹窗会打开,我们想在弹窗打开后又将按钮变成灰色,然后文字变成“快去登录”

    1. ES5实现方法
    
    //旧的逻辑
    function openModal() {
        const modal = new Modal()
        modal.style.display = 'block'
    }
    //新的逻辑
    function changeButtonText() {
        // 按钮文案修改逻辑
        const btn = document.getElementById('open')
        btn.innerText = '快去登录'
    }
    function disableButton() {
        // 按钮置灰逻辑
        const btn =  document.getElementById('open')
        btn.setAttribute("disabled", true)
    }
    function changeButtonStatus() {
        // 新版本功能逻辑整合
        changeButtonText()
        disableButton()
    }
    
    document.getElementById('open').addEventListener('click', function() {
        openModal()
        changeButtonStatus()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1. ES6实现方法
    class OpenButton {
        // 点击后展示弹框(旧逻辑)
        onClick() {
            const modal = new Modal()
        	modal.style.display = 'block'
        }
    }
    // 定义按钮对应的装饰器(新逻辑)
    class Decorator {
        // 将旧的逻辑传入
        constructor(open_button) {
            this.open_button = open_button
        }
        
        onClick() {
            this.open_button.onClick()
            // “包装”了一层新逻辑
            this.changeButtonStatus()
        }
        
        changeButtonStatus() {
            this.changeButtonText()
            this.disableButton()
        }
        
        disableButton() {
            const btn =  document.getElementById('open')
            btn.setAttribute("disabled", true)
        }
        
        changeButtonText() {
            const btn = document.getElementById('open')
            btn.innerText = '快去登录'
        }
    }
    
    document.getElementById('open').addEventListener('click', function() {
        // openButton.onClick()
        decorator.onClick()
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    ES7中的装饰器

    下面的代码可能有的浏览器不支持,可以在babeljs中转成js代码再运行。

    function classDecorator(target){
      target.hasDecorator = true;
      return target;
    }
    @classDecorator
    class Button {}
    
    
    console.log('Button是否被装饰了', Button.hasDecorator);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    适配器模式

    适配器模式就是将一个类的接口变换成所期待的另一种接口。
    例如:我有一个圆孔的耳机,需要插到方形耳机孔的手机上面,就需要一个适配器来让手机能够插上耳机。
    封装一个fetch

    export default class Http {
      //get方法
      static get(url){
        return new Promise((resolve, reject)=>{
          fetch(url)
          .then(response=>response.json())
          .then(result=>{
            resolve(result);
          })
          .catch(error=>{
            reject(error);
          })
        })
      }
      //post方法
      static post(url, data){
        return new Promise((resolve, reject)=>{
          fetch(url, {
            method: 'POST',
            headers: {
              Accept: 'application/json',
              'Content-Type': 'application/x-www-wform-urlencoded'
            },
            body: this.changeData(data);
          })
          .then(response=>response.json())
          .then(result=>{
            resolve(result);
          })
          .catch(error=>{
            reject(error);
          })
        })
      }
      //body请求体的格式化方法
      static changeData(obj){
        var prop,str = '';
        var i = 0;
        for(prop in obj){
          if(!prop){
            return;
          }
          if(! === 0){
            str += prop + '=' + obj[prop];
          }else{
            str += '&' + prop + '=' + obj[prop];
          }
          return str;
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    1. fetch适配ajax
      ajax的请求写法如下
    Ajax('get', url, data, function(data){
        //成功的回调逻辑
    },function(error){
        //失败的回调逻辑
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果我们想用上面的fetch修改ajax请求,如果一个一个修改的话十分麻烦,这时候我们就可以写一个适配器

    async function AjaxAdapter(type, url, data, success, failed){
      const type = type.toUpperCase();
      let result;
      try {
        if(type === 'GET'){
          result = await Http.get(url) || {};
        }else if(type === 'POST'){
          result = await Http.post(url, data) || {};
        }
        //假设请求对应的状态码是200
        result.code === 200 && success ? success(result) : failed(result.code);
      } catch (error) {
        if(failed){
          failed(error.code);
        }
      }
    }
    
    async function Ajax(type, url, data, success, failed){
      await AjaxAdapter(type, url, data, success, failed);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    代理模式

    代理模式就是在某种情况下,一个对象不能直接访问另一个对象,需要一个第三者(代理)来间接达到访问目的

    const girl = {
      a: 1
    };
    const proxy = new Proxy(girl, {
      get: (girl, key)=>{
        console.log(gril, 'key='+key);
      },
      set: (gril, key)=>{
        console.log(gril, key);
      }
    });
    
    const a = girl.a;
    proxy.a = 2;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    代理模式的实践

    1. 事件代理
      下面的代码实现了,点击哪个a标签就弹窗显示对应a标签上的字,但是a标签多的情况下,需要给每个a标签绑定事件,那么性能的开销就会更大。
    
    
    
      
      
      
      事件代理
    
    
      
    1 2 3 4 5 6 7 8 9 10
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    我们使用事件代理来修改js代码

    const father = document.getElementById('father');
        father.addEventListener('click', function(e){
          if(e.target.tagName === 'A'){
            e.preventDefault();
            alert(`我是${e.target.innerText}`);
          }
        });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. 虚拟代理-实现图片的懒加载
    
    
    
      
      
      
      虚拟代理
    
    
      11
      
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    1. 缓存代理
    const addAll = function(){
      console.log('进行一次新计算');
      let result = 0;
      const len = arguments.length;
      for(let i=0;i
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    1. 保护模式
      保护模式就是我们之前演示的代理模式,在getter和setter中进行拦截。

    策略模式

    例:如果输入的是a就执行a的逻辑,如果是b就执行b的逻辑,我们可以如下这样写。

    function ans(tag){
      if(tag==='a'){
        //执行a的逻辑
      }else if(tag==='b'){
        //执行b的逻辑
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如果逻辑再多一点,上面的代码逻辑就太胖了,而且违背了“开放闭合原则”。可以修改成一下格式。

    1. 使用职责分离修改代码
    function a(){
      //执行a的逻辑
    }
    function b(){
      //执行b的逻辑
    }
    function ans(tag){
      if(tag==='a'){
        a();
      }
      if(tag==='b'){
        b();
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 上面的代码是经过开放封闭改造过的,如果想在ansObj里面写新的逻辑,就可以直接通过ansObj.属性,来添加
    const ansObj = {
      a(){
        //执行a的逻辑
      },
      b(){
        //执行b的逻辑
      }
    }
    
    function ans(tag){
      return ansObj[tag]();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    状态模式

    状态模式和策略模式十分的相识。我们还拿策略模式的例子举例。

    1. 使用职责分离修改代码
    class ans {
      constructor(){
        this.state = 'init';
      }
      changeState(state){
        this.state = state;
        if(state==='a'){
          this.a();
        }else if(state==='b'){
          this.b();
        }
      }
      a(){
        //执行a的逻辑
      }
      b(){
        //执行b的逻辑
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 使用开放封闭修改代码
    const ansObj = {
      a(){
        //执行a的逻辑
      },
      b(){
        //执行b的逻辑
      }
    }
    
    class Ans {
      constructor(){
        this.state = 'init';
      }
      changeState(state){
        this.state = state;
        if(!ansObj[state])return;
        ansObj[state]();
      }
    }
    
    const test = new Ans();
    test.changeState('a');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 修改开放封闭的代码,让Ans于ansObj建立联系
    class Ans {
      constructor(){
        this.state = 'init';
      }
      const ansObj = {
        that:this,//在对象中使用this.that指向Ans中的this
        a(){
          //执行a的逻辑
        },
        b(){
          //执行b的逻辑
        }
    }
      changeState(state){
        this.state = state;
        if(!this.ansObj[state])return;
        this.ansObj[state]();
      }
    }
    
    const test = new Ans();
    test.changeState('a');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    观察者模式(发布-订阅模式)

    观察者模式是一种一对多的依赖关系,让多个观察者同时监听一个目标对象,如果目标对象状态发生变化时,回通知所有观察者,使观察者可以自动更新。

    //定义发布者类
    class Publisher {
      constructor(){
        this.observers = [];
        console.log('created');
      }
      //增加订阅者
      add(observer){
        this.observers.push(observer);
        console.log('add');
      }
      //移除订阅者
      remove(observer){
        this.observers.forEach((item, i)=>{
          if(item === observer){
            this.observers.splice(i, 1);
          }
        })
        console.log('remove');
      }
      //通知所有订阅者
      notify(){
        this.observers.forEach((observer)=>{
          observer.update(this);
        })
        console.log('invoked');
      }
    }
    
    //定义订阅者类
    class Observer {
      constructor(){
        console.log('Observer created');
      }
      update(){
        console.log('Observer update');
      }
    }
    
    //定义一个具体的发布类
    class PrdPublicher extends Publisher {
      constructor(props) {
        super(props);
        this.prdState = null;
        console.log('PrdPublicher created');
      }
      //获取当前的prdState
      getState(){
        console.log('PrdPublicher get');
        return this.prdState;
      }
      //设置prdState的值
      setState(state){
        this.prdState = state;
        this.notify();
        console.log('PrdPublicher set');
      }
    }
    
    class DeveloperObserver extends Observer {
      constructor() {
          super()
          // 需求文档一开始还不存在,prd初始为空对象
          this.prdState = {}
          console.log('DeveloperObserver created')
      }
      
      // 重写一个具体的update方法
      update(publisher) {
          console.log('DeveloperObserver.update invoked')
          // 更新需求文档
          this.prdState = publisher.getState()
          // 调用工作函数
          this.work()
      }
      
      // work方法,一个专门搬砖的方法
      work() {
          // 获取需求文档
          const prd = this.prdState
          // 开始基于需求文档提供的信息搬砖。。。
          console.log('996 begins...')
      }
    }
    
    //目标值
    const aim = new PrdPublicher();
    //观察者
    const view1 = new DeveloperObserver();
    const view2 = new DeveloperObserver();
    
    //一对多,让一个目标值对应多个观察者
    aim.add(view1);
    aim.add(view2);
    
    //当目标值中的值改变,观察者对应的值也都自动更新
    aim.setState('1');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    迭代器模式

    迭代器模式提供一种顺序访问一个对象中的各个元素,但是又不暴露该对象内部的表示。

    1. 通过forEach方法遍历一个数组
    const arr = [1,2,3];
    arr.forEach((item,index)=>{
      console.log(`索引为${index}的元素是${item}`);
    })
    
    • 1
    • 2
    • 3
    • 4

    forEach不是万能的,如果用forEach遍历伪数组则会报错。
    2. ES6对迭代器的实现
    在ES6中不仅有Array(数组)和Object(对象),还新增了Map和Set。所以ES6也推出了一套统一的接口机制——迭代器(Iterator)。

    const arr = [1,2,3];
    const iterator = arr[Symbol.iterator]();
    console.log(iterator.next());
    console.log(iterator.next());
    console.log(iterator.next());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. for…of…就是反复调用迭代器对象的next方法。
    const arr = [1,2,3];
    for(item of arr) {
        console.log(`当前元素是${item}`)
    }
    
    • 1
    • 2
    • 3
    • 4
    1. ES6实现一个迭代器生成函数
    function *iteratorGenerator(){
      yield '1'
      yield '2'
      yield '3'
    }
    const iterator2 = iteratorGenerator();
    console.log(iterator2.next());
    console.log(iterator2.next());
    console.log(iterator2.next());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. ES5实现一个迭代器生成函数
    const arr = [1,2,3];
    function iteratorGenerator2(list){
      //记录当前的索引
      var idx = 0;
      //集合的长度
      var len = list.length;
      return {
        next: function(){
          //如果索引值没有超过集合长度,done为false
          var done = idx >= len;
          //如果done为false则可以继续取值
          var value = !done ? list[idx++] : undefined;
          //将当前值和是否完毕(done)返回
          return {
            done: done,
            value: value
          }
        }
      }
    }
    
    var iterator3 = iteratorGenerator2(arr);
    console.log(iterator3.next());
    console.log(iterator3.next());
    console.log(iterator3.next());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    GAMES104-如何构建游戏世界
    Android 11.0 无源码apk授予QUERY_ALL_PACKAGES权限
    Flink学习12:DataStreaming API
    【Python爬虫】爬取2022软科全国大学排行榜
    云原生之使用Docker部署Laverna笔记工具
    STM32学习笔记:IWDG_独立看门狗
    河北海格里斯HEGERLS箱式四向穿梭车物流解决方案|5G人工智能机器人自动化立体库
    漏洞预警|CVE-2023-38545 Curl 和 libcurl 堆缓冲区溢出漏洞
    C语言printf中%s、%*s、%*.*s的作用以及实现一个进度条
    云原生之K8S------K8S亲和,反亲和、污点、容忍
  • 原文地址:https://blog.csdn.net/qq_51965698/article/details/126471544