zoukankan      html  css  js  c++  java
  • javaScript设计模式简记(4)-技巧型设计模式

    1.链模式

    链模式(Operate of Responsibility):通过在对象方法中将当前对象返回,实现对同一个对象多个方法的链式调用。从而简化对该对象的多个方法的多次调用时,对该对象的多次引用。

    JavaScript中的链模式的核心思想就是通过在对象中的每个方法调用执行完毕后返回当前对象this来实现的。由于链模式使得代码紧凑简洁而高效,在工作中已经得到很广泛的应用。当然当前主流代码库都以该模式作为自己的一种风格。 例如jQuery 等等。所以说熟知链模式的工作原理,在我们实际工作中是很有帮助的。

    研究Jquery源码去(后续补充-手把手学会搭建自己的jq库)

    2.委托模式

    委托模式(Entrust): 多个对象接收并处理同一请求,他们将请求委托给另-一个对象统一处理请求。

    典型:事件委托机制

    委托模式是通过委托者将请求委托给被委托者去处理实现的。因此委托模式解决了请求与委托者之间的耦合。通过被委托者对接收到的请求的处理后,分发给相应的委托者去处理。在JavaScript中,委托模式已经得到很广泛的应用,尤其在处理事件上,当然委托模式是一种基础技巧,因此也同样在其他设计模式中被引用,如状态模式中状态对象对接收的状态处理,策略模式中策略对象对接收到的算法处理,命令模式中命令对象对接收到的命令处理等。

    3.数据访问对象模式

    数据访问对象模式(Data access object-DAO):抽象和封装对数据源的访问与存储,DAO通过对数据源链接的管理方便对数据的访问与存储。

    数据访问对象(DAO)模式即是对数据库的操作(如简单的CRUD创建、读取、更新、删除)进行封装,用户不必为操作数据库感到苦恼,DAO已经为我们提供了简单而统一的操作接口。并且对于使用者来说,不必了解DAO内部操作是如何实现的,有时甚至不必了解数据库是如何操作的。对于后端数据库来说(如MongoDB),DAO对象甚至会保留对数据库的链接,这样我们每次操作数据库时不必一次次地向数据库发送链接请求。DAO是一个对象,因此它封装了属性和方法,并通过这些属性与方法管理着数据库。因此有时为了实现需求我们还可以对DAO对象进行拓展。但是更佳实践是对DAO做一层适用于自己的封装,这样在团队开发中不会影响到他人的使用。

    (后续补充-手把手学会数据库封装操作)

    4.节流模式

    节流模式(Throttler): 对重复的业务逻辑进行节流控制,执行最后一次操作并取消其他操作,以提高性能。

    构造节流器的思路是这样的:首先节流器应该能做两件事情,第--件事情就是清除将要执行的函数,此时要对节流器传递两个参数(是否清除,执行函数), 如果第一个参数为true,则表示清除将要执行的函数。同时会判断第二个参数(执行函数)有没有计时器句柄,有则清除计时器。节流器能做的第二件事情就是延迟执行函数。此时要传递两个参数(执行函数,相关参数)。在节流器内部首先要为执行函数绑定一个计时 器句柄,来保存该执行函数的计时器,对于第二个参数一相关参 数来说,大致包括3个部分,执行函数在执行时的作用域、执行函数的参数、执行函数延迟执行的时间。

    对于DOM的操作,常常会占用大量的内存资源和cpu处理时间。甚至大量的DOM操作在一些浏览器中也很可能导致浏览器的崩溃。由于JavaScript的单线程处理机制,导致DOM操作占用大量资源时会严重堵塞后面重要程序的执行。节流模式的核心思想是创建计时器,延迟程序的执行。这也使得计时器中回调函数的操作异步执行(这里的异步执行并不是说JavaScript 是多线程语言,JavaScript 从设计之初就是单线程语言,异步只是说脱离原来程序执行的顺序,看上去,异步程序像是在同时执行。但是某一时刻,当前执行的程序一定是所有异步程序( 包括原程序)中的某一个)。由此可看出节流模式主要有两点优势:第一,程序能否执行是可控的。执行前的某一时刻是否清除计时器来决定程序是否可以继续执行。第二,程序是异步的。由于计时器机制,使得程序脱离原程序而异步执行(当然随着worker技术的兴起,也可开启多线程模式实现),因此不会影响后面的程序的正常执行。在其他方面,比如对异步请求(ajax) 应用节流,此时可以优化请求次数来节省资源。最后,对于在节流器中对计时器的设置,有的人可能感觉直接绑定在原函数会暴露计时器句柄,这使得外部可修改。当然你也可以将节流器改造成类的形式,将计时器句柄作为私有变量存放在类内部。

    5.简单模板模式

    简单模板模式(Simple template): 通过格式化字符串拼凑出视图避免创建视图时大量节点操作。优化内存开销。

    // 模板渲染方法
    A.formateString = function(str, data) { return str.replace(/{# (w+)#}/g, function (match, key) { return typeof data[key] === undefined ? '' : data[key]; }) ; }

    简单模板模式意在解决运用DOM操作创建视图时造成资源消耗大、性能低下、操作复杂等问题。用正则匹配方式去格式化字符串的执行的性能要远高于DOM操作拼接视图的执行性能,因此这种方式常备用于大型框架(如MVC等)创建视图操作中。简单模板模式主要包含三部分,字符串模板库,格式化方法,字符串拼接操作。然而前者,在不同需求的实现中,视图往往是不一-致的, 因此字符串模板常常是多变的,如何更好地创建模板,给了我们极大的灵活性,对于字符串格式化方法在一个项目中通常是不变的,团队中所有成员都应该以同种方式去格式化模板才能使模板更易读。对于字符串拼接操作,常常是随需求中的视图变化而变化,这里对拼接的灵活的运用可以使创建的视图过程更高效,模板复用率更高。

    6.惰性模式

    惰性模式(layier): 减少每次代码执行时的重复性的分支判断,通过对对象重定义来屏蔽原对象中的分支判断。

    方法一:加载即执行

     方法二:惰性执行

    第二种实现方式与第一种实现方式的不同之处在于,首先,内部对元素dom执行能力检测并显示重写,其次,原始函数在函数的最末尾重新执行一遍来绑定事件。 不过在文件加载后A.on方法还没能重新被定义。所以还需等到某一元素绑定事件时,A.on 才能被重定义。

    惰性模式是一种拖延模式,由于对象的创建或者数据的计算会花费高昂的代价(如页面刚加载时无法辨别是该浏览器支持某个功能,此时创建对象是不够安全的),因此页面之处会延迟对这一类对象的创建。惰性模式又分为两种:第一种是文件加载后立即执行对象方法来重定义对象。第二种是当第一次使用方法对象时重定义对象。对于第一种方式,由于文件加载时执行,因此会占用一些资源。对于第二种方式由于在第一次使用时重定义对象,以致第一次执行时间增加。有时候两种方式对资源的开销都是可接受的,因此到底使用哪种方式,要看具体需求而定。

    7.参与者模式

    参与者(participator): 在特定的作用域中执行给定的函数,并将参数原封不动地传递。

     bind浏览器兼容写法

    参与者模式实质上是两种技术的结晶,函数绑定和函数柯里化。早期浏览器中并未提供bind方法,因此聪明的工程师们为了使添加的事件能够移除,事件回调函数中能够访问到事件源,并且可以向事件回调函数中传入自定义数据,才发明了函数绑定与函数柯里化技术。对于函数绑定,它将函数以函数指针(函数名)的形式传递,使函数在被绑定的对象作用域中执行,因此函数的执行中可以顺利地访问到对象内部的数据,由于函数绑定构造复杂,执行时需消耗更多的内存,因此执行速度上要稍慢一些。不过相对于解决的问题来说这种消耗还是值得的,因此它常用于事件,setTimeout 或setInterval等异步逻辑中的回调函数。对于函数柯里化即是将接受多个参数的函数转化为接受一部分参 数的新函数,余下的参数保存下来,当函数调用时,返回传入的参数与保存的参数共同执行的结果。通常保存下来的参数保存于闭包内,因此函数柯里化的实现要消耗一定的资源。函数的柯里化有点类似类的重载,不同点是类的重载是同一个类对象,函数的柯里化是两个不同的函数。随着函数柯里化的发展,现在又衍生出一种反柯里化的函数,其目的是方便我们对方法的调用。

    8.等待者模式

    等待者模式(waiter):通过对多个异步进程监听,来触发未来发生的动作。

    //等待对象
    var Waiter = function() {
      //注册了的等待对象容器
      var dfd = [],
      //成功回调方法容器
      doneArr = [],
      //失败回调方法容器
      failArr = [],
      //缓存Array方法slice
      slice = Array.prototype.slice,
      //保存当前等待者对象
      that = this;
      //监控对象类
      var Primise = function() {
        //监控对象是否解决成功状态
        this.resolved = false;
        //监控对象是否解决失败状态
        this.rejected = false;
      }
      //监控对象类原型方法
      Primise.prototype = {
        // 解决成功
        resolve: function(){},
        //解决失败
        reject: function() {}
      }
      //创建监控对象
      that.Deferred = function() {
        return new Primise();
      }
      //回调执行方法
      function _exec (arr) {}
      //监控异步方法参数:监控对象
      that.when = function() {};
      //解决成功回调函数添加方法
      that.done = function() {};
      //解决失败回调函数添加方法
      that.fail = function() {};
    }

    等待者对象内部定义了三个数组,分别是监控对象容器,以及成功与失败回调函数容器;一个类监控对象,该对象有两个属性,即监控解决成功状态与监控解决失败状态,两个方法,解决成功方法与解决失败方法:一个私有方法_exec 来处理成功失败回调函数的方法; 3个共有方法接口: when方法监控异步逻辑,done方法添加成功回调函数,fail方法添加失败回调函数。

    具体实现

    //监控对象原型方法
    Primise.prototype = {
      //解决成功
      resolve: function() {
        //设置当前监控对象解决成功
        this.resolved = true;
        //如果没有监控对象则取消执行
        if (!dfd.length)
          return;
        //遍历所有注册了的监控对象
        for(var i = dfd.length-1; i >= 0; i--) {
          //如果有任意一个监控对象没有被解决或者解决失败则返回
          if(dfd[i] && !dfd[i].resolved || dfd[i].rejected) {
            return;
          }
          //清除监控对象
          dfd.splice(i,1);
        }
        //执行解决成功回调方法
        _exec(doneArr);
      },
      //解决失败
      reject: function() {
        //设置当前监控对象解决失败
        this.rejected = true;
        //如果没有监控对象则取消执行
        if (!dfd.length)
          return;
        //清除所有监控对象
        dfd.splice(0);
        //执行解决失败回调方法
        _exec(failArr);
      }
    }

    _exec回调执行方法

    //回调执行方法
    function _exec(arr) {
        var i = 0,
        len = arr.length;
        //遍历回调数组执行回调
        for(; i < len; i++) {
            try{
                //执行回调函数
                arr[i] && arr[i]();
            }catch(e){}
      }
    }

    when方法

    //监控异步方法参数:监控对象
    that.when = function() {
      // 设置监控对象
      dfd = slice.call(arguments) ;
      //获取监控对象数组长度
      let i = dfd.length;
      //向前遍历监控对象,最后一个监控对象的索引值为length-1
      for(--i; i >= 0; i--) {
        //如果不存在监控对象,或者监控对象已经解决,或者不是监控对象
        if(!dfd[i] || dfd[i].resolved || dfd[i].rejected || !dfd[i] instanceof Primise) {
          //清理内存清除当前监控对象
          dfd.splice(i,1);
        }
      }
      //返回等待者对象
      return that; 
    };

    done和fail方法

    //解决成功回调函数添加方法
    that.done = function() {
      //向成功回调函数容器中添加回调方法
      doneArr = doneArr.concat(slice.call(arguments)) ;
      //返回等待者对象
      return that;
    };
    //解决失败回调函数添加方法
    that.fail = function() {
      //向失败回调函数容器中添加回调方法
      failArr = failArr.concat(slice.call(arguments)) ;
      //返回等待者对象
      return that;
    }

    实例1:

    var waiter = new Waiter() ;
    //要在方法执行内部创建监听对象
    //第一个彩蛋,5秒后停止
    var first = function() {
      //创建监听对象
      let dtd = waiter.Deferred();
      setTimeout(function() {
        console.log('first finish');
        //发布解决成功消息
        dtd.resolve();
      }, 5000) ;
      //返回监听对象
      return dtd;
    }()
    //第二个彩蛋,10秒后停止
    var second = function() {
      //创建监听对象
      let dtd = waiter.Deferred();
      setTimeout(function() {
        console.log('second finish');
        //发布解决成功消息
        dtd.resolve();
      }, 10000) ;
      //返回监听对象
      return dtd;
    }();
    // 用等待者对象监听两个彩蛋的工作状态,并执行相应的成功回调函数与失败回调函数
    waiter
    //监听两个彩蛋
    .when(first, second)
    //添加成功回调函数
    .done(function() {
      console.log('success');
    }, function() {
      console.log('success again');
    })
    //添加失败回调函数
    .fail(function() {
      console.log('fail');
    });
    //输出结果
    // first
    // second
    // success
    // success again

    实例2:封装异步ajax请求

    //封装get请求
    var ajaxGet = function(url, success, fail) {
      var xhr = new XMLHttpRequest() ;
      //创建检测对象
      var dtd = waiter.Deferred();
      xhr.onload = function (event) {
        //请求成功
        if((xhr.status>= 200 && xhr.status < 300) || xhr.status == 304) {
          success && success()
          dtd.resolve();
        }else{
          //请求失败
          dtd.reject(); 
          fail && fail();
        }
      };
      xhr.open("get", url, true) ;
      xhr.send(null) ;
    };

    等待者模式意在处理耗时比较长的操作,比如canvas中遍历并操作一张大图片中的每一个像素点、定时器操作、异步请求等。等待者模式为我们提供了一个抽象的非阻塞的解决方案,通过创建Primise对象,对耗时逻辑的未来状态变化返回一个响应,通过在等待者对象内部捕获这些响应信息,为耗时较长的操作提供了回调方案,使我们可以捕获耗时操作完成时或中断时的状态并执行相应的回调方案。

  • 相关阅读:
    Cypress web自动化18-cypress.json文件配置baseUrl
    Linux学习29-awk提取log日志信息,统计日志里面ip访问次数排序
    docker学习14-配置 docker 阿里云/腾讯云加速器
    Cypress web自动化17-fixture加载json文件数据
    Cypress web自动化16-参数化,数据驱动测试案例
    Cypress web自动化15-Hooks使用方法
    Cypress web自动化14-window窗口属性
    Cypress web自动化13-viewport设置不同分辨率,适配不同设备,手机型号
    Cypress web自动化12-父子元素定位
    Eclipse安装scala
  • 原文地址:https://www.cnblogs.com/LeoXnote/p/13055544.html
Copyright © 2011-2022 走看看