JsDesignModel第五篇

技巧型设计模式
    通过特定技巧解决组件某些方面的问题
    1.链模式
        在当前对象方法中将当前对象返回,实现对同一个对象多个方法的链式调用
        1.1深究jQuery
            链模式基于原型继承,在每一个原型方法的实现上都返回当前对象this
            使当前对象一直处于原型链作用域的顶端
        1.2.原型式继承
       

     var A = function(){}A.prototype = {length : 2;size : function(){return this.length;}}

            这里我们要访问size方法
       

     var a = new A();console.log(a.size());

            size绑定在A的原型上,没有绑定在自己身上
            A函数对象执行的结果没有返回值
        1.3找一位助手
        

    var A = function(){return B;}var B = A.prototype = {length : 2;size : function(){return this.length;}}

            这时候访问console.log(A().size());
            jQuery中为了减少变量的创建,索性将B对象看做事A的一个属性设置
          

  var A = function(){return A.fn;}A.fn = A.prototype = {}

        1.4获取元素
            jQuery的目的是为了获取元素,返回元素簇(元素的聚合对象)
            这里返回一个A.fn对象,如果可以添加一个init方法获取元素
            然后在A中返回获取到的元素
           

 var A =function(selector){return A.fn.init(selector);}A.fn = A.prototype = {init : function(selector){return document.getElementById(selector)},length : 2,size : function(){return this.length;}}console.log(A('demo'));//<div id="demo"></div>

        1.5一个大问题
            A对象返回的结果还能够拥有A.fn中的方法,比如这里的size
            this指代当前对象,直接在init中将this返回即可
            对象可以设置属性,通过this对象将元素设置成为当前对象的一个属性
            像访问数组一样可以将它们的属性值顺序设置为数字索引
        

    var A =function(selector){return A.fn.init(selector);}A.fn = A.prototype = {init : function(selector){//作为当前对象的属性值保存this[0] = document.getElementById(selector);//校正length属性this.length = 1;//返回当前对象return this;},length : 2,size : function(){return this.length;}}

        1.6覆盖获取
            后面的元素覆盖前面的元素
            因为我们每次在A的构造函数中返回的A.fn.init(selector);
            对象指向同一个对象造成的
        

    var A =function(selector){return new A.fn.init(selector);}

        1.7方法丢失
        1.8丰富元素获取
            获取某类元素
           

var A =function(selector, context){return new A.fn.init(selector);}A.fn = A.prototype = {constructor : A,init : function(selector, context){//获取元素长度this.length = 0,//默认获取元素的上下文为documentcontext = context || document;if(~selector.indexOf('#')){this.[0] = document.getElementById(selector.slice(1));this.length = 1;}else{var doms = context.getElementsByTagName(selector),i = 0,len = doms.length;for(; i < len; i++){this[i] = doms[i];}this.length = len;}this.context = context;this.selector = selector;return this;},......}

    2.委托模式
        多个对象接受并处理同一请求,将请求委托给另一个对象统一处理请求
        2.1委托父元素
            从事件捕获开始,到触发该事件,再到事件冒泡阶段
            可以将子元素的事件委托给更高层面上的父元素去绑定执行
        

    ul.onclick = function(e){var e = e || window.event,tar = e.target || e.srcElement;if(tar.nodeName.toLowerCase() === 'li'){tar.style.backgroundColor = 'grey';}}

            li元素共有一个ul父元素,将点击事件绑定给父元素
            通过事件冒泡就可以实现事件的传递,子元素的点击事件可以传到父元素
            我们只需要监听父元素的点击事件,然后判断元素是不是我们寻找的元素
            事件委托:将子元素的事件委托给父元素,通过事件冒泡就可以实现事件的传递
        2.2预言未来
            当前页面不存在,在未来会添加,将未来元素事件绑定给现有父元素
        2.3内存外泄
            老版本IE采用计数式垃圾回收机制,那些对DOM元素的引用没有显性清除的数据会遗留在内存中
            除非关闭浏览器
        2.4数据分发
            与后端处理数据,各个模块都需要发起请求,浪费资源,等待时间长
            我们将所有请求打包,委托一个对象发送
            委托对象再拆包发给每一个模块
    3.数据访问对象模式
        抽象和封装对数据源的访问与存储,DAO通过对数据源链接的管理方便对数据的访问与存储
        3.1数据访问对象类
            对本地数据的一次封装,创建DAO类
            别人想使用本地存储,会知道你保存的那些数据?
            本地存储数据库不同于服务器端关系型数据库
            将数据保存在localStorage这个对象中,这是一个大容器,对于同一个站点
            里面没有分割库,别人和你使用的是一个库
            所以你需要将每次存储的数据字段前面添加前缀标识'分割'localStorage存储
            本地存储对数据的保存实际上是localStorage的一个字符串属性
            对于本地存储来说了解存储时间很有必要,方便日后对数据的管理,比如定时清除
            可以添加时间戳,每个人存储的数据内容不同
            时间戳与将要存储的数据都是属性字符串,所以需要一个拼接符
        3.2创建DAO类
         

   var BaseLocalStorage = function(preId, timeSign){//本地存储数据库前缀this.preId = preId;//定义时间戳与存储数据之间的拼接符this.timeSign = timeSign || '-';}

        3.3数据操作状态
            在内部保存操作返回状态供以后使用
         

   //本地存储类原型方法BaseLocalStorage.prototype = {//操作状态status : {SUCCESS : 0,     //成功FAILURE : 1,     //失败OVERFLOW : 2,    //溢出TIMEOUT : 3      //过期},//保存本地存储链接storage : localStorage || window.localStorage,getKey : function(key){return this.preId + key;},set : function(key, value, callback, time){//...},get : function(key, callback){//...},remove : function(key, callback){//...}}

        3.4增添数据
          

  set : function(key, value, callback, time){//默认操作状态为成功var status = this.status.SUCCESS,//获取真实字段key = this.getKey(key);try{//参数事件参数时获取事件戳time = new Date(time).getTime() || time.getTime();}catch(e){//为传入时间参数或者时间参数有误获取默认时间:一个月time = new Date().getTime() + 1000 * 60 * 60 * 24 * 31;}try{//向数据库添加数据this.storage.setItem(key, time + this.timeSign + value);}catch(e){//溢出失败,返回溢出状态status = this.status.OVERFLOW;}//有回调函数则执行回调函数并传入参数操作状态,真实数据字段标识以及存储数据值callback && callback.call(this, status, key, value);}

        3.5查找数据
         

   get : function(key, callback){var status = this.status.SUCCESS,key = this.getKey(key),value = null,timeSignLen = this.timeSign.length,that = this,index,time,result;try{value = that.storage.getItem(key);}catch(e){result = {status : that.status.FAILURE,value : null};callback && callback.call(this, result.status, result.value);return result;}if(value){index = value.indexOf(that.timeSign);time = +value.slice(0, index);if(new Date(time).getTime() > new Date().getTime() || time == 0){value = value.slice(index + timeSignLen);}else{value = null,status = that.status.TIMEOUT;that.remove(key);}}else{status = that.status.FAILURE;}result = {status : status,value : value};callback && callback.call(this, result.status, result.value);return result;}

        3.6删除数据
        

    remove : function(key, callback){var status = this.status.FAILURE,key = this.getKey(key),value = null;try{value = this.storage.getItem(key);}catch(e){}if(value){try{this.storage.removeItem(key);status = this.status.SUCCESS;}catch(e){}}callback && callback.call(this, status, status > 0 ? null :value.slice(value.indexOf(this.timeSign) + this.timeSign.length))}

        3.7检验DAO
            有些方法默认操作状态是成功,有些默认操作状态是失败
    4.节流模式
        对重复的业务逻辑进行节流控制,执行最后一次操作并取消其他操作,以提高性能
        4.1屏蔽重复的事情(业务逻辑)只执行最后一次
            比如浏览器触发scroll事件而执行了多次该事件回调函数
        4.2节流器
        

    //节流器var throttle = function(){//获取第一个参数var isClear = arguments[0], fn;//如果第一个参数是boolean类型那么第一个参数则表示是否清除计时器if(typeof isClear === 'boolean'){//第二个参数则为函数fn = arguments[1];//函数的计时器句柄存在,则清除该计时器fn.__throttleID && clearTimeout(fn.__throttleID);//通过计时器延迟函数的执行}else{//第一个参数为函数fn = isClear;//第二个参数为函数执行时的参数param = arguments[1];//对执行时的参数适配默认值,这里使用extendvar p = extend({context : null,  //执行函数执行时的作用域args : [],       //执行函数执行时的相关参数time : 300       //执行函数延迟执行的时间, param);//清除执行函数计时器句柄arguments.callee(true, fn);//为函数绑定计时器句柄,延迟执行函数fn.__throttleID = setTimeout(function(){//执行函数fn.apply(p.context, p.args)}, p.time)}}

            两件事情
                1.清除要执行的函数,传递两个参数(是否清除, 执行函数)
                  第一个参数为true表示清除,同时判断第二个有木有计时器句柄
                  有也就清除
                2.延迟执行函数,传递两个参数(执行函数, 相关参数)
                   为执行函数绑定一个计时器句柄,来保存执行函数的计时器
                   相关参数,执行时的作用域、执行函数的参数、执行函数延迟执行的时间  
    5.简单模板模式
        通过格式化字符串拼凑出视图避免创建视图时大量节点操作
        5.1创建模板,用数据去格式化字符串来渲染视图并插入到容器里
          就像卡片拼图游戏,首先得有一个渲染方法,想渲染模板就要一个渲染器
          比如数据对象'{demo:"this is a demo"}'去格式化
          '<a>{#demo#}</a>+'字符串模板,得到最后的'<a>this is a demo</a>'
          然后可以插入到页面中,的需要一个渲染模板引擎方法
          将{#demo#}替换成数据对象中demo的属性值
        

  var A = A || {};A.root = document.getElementById('container');A.stragtegy = {'listPart' : function(){},'codePart' : function(){},'onliTitle' : function(){},'guide' : function(){}//...}//创建视图入口A.init = function(data){this.stragtegy[data.type](data);}

        5.2模板渲染方法
         

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

            //文字列表展示
        5.3模板生成器
    6.惰性模式
        减少每次代码执行时的重复性分支判断,通过对对象重定义来屏蔽原对象中的分支判断
        6.1第一次执行时已经判断过了,以后的执行是不必要的,那么在第一次执行后重新定义它
            1.文件加载进来时通过闭包执行该方法并重新定义,会占用一定资源
            2.在第一种的基础上做一次延迟
    7.参与者模式
        在特定的作用域中执行给定函数,并将参数原封不动的传递
        7.1函数绑定
       

//函数绑定bindfunction bind(fn, context){return function(){return fn.apply(context, arguments);}}//测试对象var demoObj = {title : '这是一个例子'}//测试方法function demoFn(){console.log(this.title);}//让demoObj参与demoFn的执行var bindFn = bind(demoFn, demoObj);demoFn();        //undefinedbindFn();        //这是一个例子

        demoFn在执行时,demoObj参与进来并提供了作用域
        bindFn只是让demoFn寄生其中,在执行的时候才让demoObj加入
        那么这两个还是不同的函数
        7.2函数柯里化
            是对函数的参数进行分割,有点像多态
            根据传递参数的不同,让一个函数存在多个状态
            函数柯里化处理的是函数,以函数为基础
            借助函数柯里化伪造其他函数,让伪造函数调用这个基函数完成不同功能
    8.等待者模式
        通过对多个异步进程监听,来触发未来发生的动作
        8.1解决不确定先后完成的异步逻辑
          等待所有异步逻辑的完成,自动执行成功回调函数
          一个失败就执行失败回调函数
        8.2等待者对象
           

var Waiter = function(){var dfd = [],failArr = [],slice = Array.prototype.slice,that = this;var Primise = function(){this.resolved = false;this.rejected = false;}Primise.prototype = {resolve : function(){},reject : function(){}}that.Defered = function(){return new Primise();}function _exec(arr){}that.when = function(){};that.done = function(){};that.fail = function(){}}

            内部定义了三个数组:监控对象容器、成功与失败回调函数容器
            一个类-监控对象:监控解决成功状态与监控解决失败状态
            两个方法:解决成功方法与解决失败方法
            一个私有方法_exec来处理成功失败回调函数方法
            三个共有方法接口:when方法监控异步逻辑、done/fail添加成功/失败回调函数
        8.3.监控对象
           

 Primise.prototype = {resolve : function(){}}

 

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注