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,
                    //默认获取元素的上下文为document
                    context = 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];
                    //对执行时的参数适配默认值,这里使用extend
                    var 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函数绑定
       

//函数绑定bind
        function 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();        //undefined
        bindFn();        //这是一个例子


        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(){

                }
            }
发布了34 篇原创文章 · 获赞 34 · 访问量 1096

猜你喜欢

转载自blog.csdn.net/qq_45517916/article/details/103673709