JavaScript设计模式-技巧型设计模式简介

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuelian3015/article/details/89448019

技巧型设计模式是I通过一些特定技巧来解释组件的某些问题。主要包含链模式、委托模式、数据模式、数据访问对象模式、节流模式、简单模板模式、惰性模式、参与者模式、等待者模式。

链模式

在对象方法中将当前对象返回,实现了对同一个对象多个方法链式调用。简化了在对对象多个方法的多次调用时,对该对象多次引用。在jQuery的使用中,链式调用是最平不过了。

/**
 * 链模式
 * 元素获取的模式
 * @param  selector 选择符
 * @param context  上下文
 */

var Dom = function(selector,context){
    return new Dom.fn.init(selector,context)
}

Dom.fn = Dom.prototype = {
    constructor:Dom,
    init:function(selector,context){
        //获取元素长度
        this.length = 0;

        context = context || document;
        //若是ID选择符,按位非把-1转化为0,,布尔值false
        if (~selector.indexOf("#")) {
            this[0] = document.getElementById(selector.slice(1));
            this.length = 1;
        }else{
            //是元素名称
            var doms = context.getElementsByTagName(selector);
            var i = 0;
            var len = doms.length;
            for(;i<len;i++){
                this[i] = doms[i];
            }
            this.length = len;
        }
        this.context = context;
        this.selector = selector;
        return this;
    }
}

/**
 * 对象拓展
 */
Dom.extend = Dom.fn.extend = function(){
    //从第二参数开始拓展
    var i =1;
    var len = arguments.length;
    var target = arguments[0];
    var j;
    //只传递 一个参数
    if (i == len) {
        target = this;
        i--;
    }

    for(;i<len;i++){
        for(j in arguments[i]){
            target[j] = arguments[i][j];
        }
    }

    return target

}

Dom.fn.extend({
    //添加事件
    on:(function(){
        if (document.addEventListener) {
            return function(type,fn){
                var i = this.length - 1;
                for(;i>=0;i--){
                    this[i].addEventListener(type,fn,false);
                }
                return this;
            }
        }else if(document.attachEvent){ //IE浏览器的DOM2级事件
            return function(type,fn){
                var  i = this.length - 1 ;
                for(; i>=0;i--){
                    this[i].addEvent("on"+type,fn);
                }
                return this;
            }
        }else{
            return function(type,fn){
                var i = this.length - 1;
                for(;i>=0;i--){
                    this[i]['on'+type] = fn;
                }
                return this;
            }
        }
    })()
})

Dom.extend({
    //把分割线转化为驼峰形式
    camelClass:function(str){
        return str.replace(/\-(\w)/g,function(all,letter){
            return letter.toUpperCase();
        })
    }
});

Dom.extend({
    css:function(){
        var arg = arguments;
        var len = arg.length;
        if (this.length < 1) {
            return this;
        }

        if (len === 1) {
            if (typeof arg[0] ==="string") {
                if (this[0].currentStyle) {
                    return this[0].currentStyle[name];
                }else{
                    return getComputedStyle(this[0],false)[name];
                }
            }else if(typeof arg[0] === "object"){ //设置多个样式  
                for(var i in arg[0]){
                    for(var j = this.length-1;j>=0;j--){
                        this[j].style[Dom.camelClass(i)] == arg[0][i];
                    }
                }
            }
        }else if(len === 2){ //两个参数设置一个参数
            for(var j = this.length-1;j>=0;j--){
                this[j].style[Dom.camelClass(arg[0])] = arg[1]
            }
        }
        return this;
    }
})

/**
 * 获取、设置CSS样式,传递一个参数。如果参数是字符串就返回第一个元素的CSS样式值,如果是对象,那么就为每一个元素设置多个样式;如果传递两个参数,就为每一个元素设置样式
 */
Dom.fn.extend({
    attr:function(){
        var arg = arguments;
        var len = arg.length;
         if (this.length < 1) {
             return this;
         }
         if (len === 1) {
             if (typeof arg[0] === "string") {
                 return this[0].getAttribute(arg[0]);
             }else if(typeof  arg[0] === "object"){
                 for(var i in arg[0]){
                     for(var j = this.length - 1 ;j>=0;j--){
                         this[j].setAttribute(i,arg[0][i]);
                     }
                 }
             }
         }else if(len === 2){
             //两个参数就设置每个元素的单个样式
             for(var j = this.length-1;j>=0;j--){
                 this[j].setAttribute(arg[0],arg[1]);
             }
         }
         return this;
    }
});

/**
 * 获取元素属性
 * 只传递一个参数,,如果参数是字符串就返回第一个元素属性。
 * 如果参数是对象,设置每个元素多个属性值;
 * 传递两个参数,第一个参数为属性名,第二个参数为属性值,设置每个元素的属性
 */
Dom.fn.extend({
   //获取、设置元素的内容
   html:function(){
       var arg = arguments;
       var len = arg.length;
       //没有参数就获取第一个元素的内容
       if (len === 0) {
           return this[0] && this[0].innerHTML;
       }else{
           for(var i = this.length-1;i>=0;i--){
               this[i].innerHTML = arg[0];
           }
       }
       return this;
   } 
})

委托模式(Entrust)

多个对象接受同一个请求,把请求委托给另外一个对象统一处理请求。节省了请求的时间和流量如下:

/**
 * 委托模式
 * 统一请求的处理
 */

var Deal = {
    banner:function(){},
    aside:function(){},
    article:function(){},
    memer:function(){},
    message:function(){}
}

$.get("deal.php?",function(res){
    //数据分包
    for(var i in res){
        Deal[i] && Deal[i](res[i])
    }
})

数据访问对象模式(DAO)

抽象与封装对数据源的访问和存储,DAO通过对数据源的管理方便对数据的访问和存储

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

//本地存储类原型方法
BaseLocalStorage.prototype = {
    status :{
        SUCCSS:0,   //成功
        FAULURE:1,  //失败
        OVERFLOW:2,  //溢出
        TIMEOUT:3  //过期
    },

    //保存本地数据
    storage:localStorage || window.localStorage,
    getKey:function(key){
        return this.preId+key;
    },
    /**
     * 添加(修改)数据
     * @param {*} key 数据字段标识
     * @param {*} value  数据值
     * @param {*} callback  回调函数
     * @param {*} time  添加时间
     */
    set:function(key,value,callback,time){
        //默认操作状态成功
        var status = this.status.SUCCSS;
        var key = this.getKey(key);

        try {
            //时间参数获取时间戳
            time = new Date().getTime(key) || time.getTime();
        } catch (e) {
            //传入时间参数或者时间参数有误时,默认时间为一个月
            time = new Date().getTime() + 31 * 24 * 60 * 60 * 1000;
        }

        try {
            //向数据库中添加数据
            this.storage.setItem(key,time+this.timeSign + value);
        } catch (e) {
            //溢出失败,返回溢出状态
            status = this.status.OVERFLOW;
        }
        //有回调函数就执行回调函数并且传入参数操作状态。。真实数据字段标识以及存储数据值
        callback && callback.call(this,status,key,value);
    },
    /**
     * 获取数据
     * @param {*} key 数据字段标识 
     * @param {*} callback  回调函数
     */
    get:function(key,callback){
        //默认操作状态是成功的
        var sattus = this.status.SUCCSS;
        var key = this.getKey(key);
        var value = null;
        //拼接符的长度
        var timeSignLen = this.timeSign.length;

        //缓存当前对象
        var that = this;
        //时间戳和存储数据拼接的起始位置
        var index;
        var time;
        var result;

        try {
            value = that.storage.getItem(key);
        } catch (e) {
            result = {
                status:that.status.FAULURE,
                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();
            }
        }else{
            //未获取数据字符串状态为失败状态
            status = that.status.FAULURE;
        }
        result = {
            status:status,
            value:value
        };
        callback && callback.call(this,result.status,result.value);
        return result
    },
    /**
     * 删除数据
     * @param {*} key 数据字段标识
     * @param {*} callback  回调函数
     */
    remove:function(key,callback){
        var status = this.status.FAULURE;
        var key = this.getKey(key);
        var value = null;

        try {
            value = this.storage.getItem(key);
        } catch (e) {}
        if (value) {
            try {
                this.storage.removeItem(key);

                status = this.status.SUCCSS
            } catch (e) {}
        }
        callback && callback.call(this,status,status>0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length))
    }
}

数据访问对象模式,是对数据库操作的封装。在node.js中对数据库的操作,如下实例:

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

//本地存储类原型方法
BaseLocalStorage.prototype = {
    status :{
        SUCCSS:0,   //成功
        FAULURE:1,  //失败
        OVERFLOW:2,  //溢出
        TIMEOUT:3  //过期
    },

    //保存本地数据
    storage:localStorage || window.localStorage,
    getKey:function(key){
        return this.preId+key;
    },
    /**
     * 添加(修改)数据
     * @param {*} key 数据字段标识
     * @param {*} value  数据值
     * @param {*} callback  回调函数
     * @param {*} time  添加时间
     */
    set:function(key,value,callback,time){
        //默认操作状态成功
        var status = this.status.SUCCSS;
        var key = this.getKey(key);

        try {
            //时间参数获取时间戳
            time = new Date().getTime(key) || time.getTime();
        } catch (e) {
            //传入时间参数或者时间参数有误时,默认时间为一个月
            time = new Date().getTime() + 31 * 24 * 60 * 60 * 1000;
        }

        try {
            //向数据库中添加数据
            this.storage.setItem(key,time+this.timeSign + value);
        } catch (e) {
            //溢出失败,返回溢出状态
            status = this.status.OVERFLOW;
        }
        //有回调函数就执行回调函数并且传入参数操作状态。。真实数据字段标识以及存储数据值
        callback && callback.call(this,status,key,value);
    },
    /**
     * 获取数据
     * @param {*} key 数据字段标识 
     * @param {*} callback  回调函数
     */
    get:function(key,callback){
        //默认操作状态是成功的
        var sattus = this.status.SUCCSS;
        var key = this.getKey(key);
        var value = null;
        //拼接符的长度
        var timeSignLen = this.timeSign.length;

        //缓存当前对象
        var that = this;
        //时间戳和存储数据拼接的起始位置
        var index;
        var time;
        var result;

        try {
            value = that.storage.getItem(key);
        } catch (e) {
            result = {
                status:that.status.FAULURE,
                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();
            }
        }else{
            //未获取数据字符串状态为失败状态
            status = that.status.FAULURE;
        }
        result = {
            status:status,
            value:value
        };
        callback && callback.call(this,result.status,result.value);
        return result
    },
    /**
     * 删除数据
     * @param {*} key 数据字段标识
     * @param {*} callback  回调函数
     */
    remove:function(key,callback){
        var status = this.status.FAULURE;
        var key = this.getKey(key);
        var value = null;

        try {
            value = this.storage.getItem(key);
        } catch (e) {}
        if (value) {
            try {
                this.storage.removeItem(key);

                status = this.status.SUCCSS
            } catch (e) {}
        }
        callback && callback.call(this,status,status>0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length))
    }
}

/**
 * 在NodeJS的配置项中设置
 * config.js
 */

module.exports = {
    DB:{
        db:"demo",
        host:"localhost",
        port:3306
    }
}

/**
 * db.js
 * 引用mongodb模块
 */
var mongodb = require("mongedb");
var config = require("./config").DB;

var d = new mongodb.Db(
    config.db,  //数据库名
    new mongodb.Server(
        config.host,  //主机
        config.port, //端口号
        {auto_reconnect:true}  //自动连接
    ),
    {safe:true}
);

exports.DB = function(DB){
    return {
        /**
         * 插入数据
         * @param data
         * @param success
         * @param fail  
         */
        insert:function(data,success,fail){
            connect(col,function(col,db){
                col.insert(data,function(err,docs){
                    if(err){
                        fail && fail(err);
                    }else{
                        success && success(docs);
                    }
                    db.close();
                });
            });
        },
        /**
         * 删除数据
         * @param data
         * @param success 
         * @param faill
         */
        remove:function(data,success,fail){
            connect(col,function(col,db){
                col.remove(data,function(err,len){
                    if(err){
                        fail && fail(err);
                    }else{
                        success && success(len);
                    }
                    db.close()
                })
            })
        },
        /**
         * 更新数据
         * @param con 
         * @param doc
         * @param success 
         * @param faill
         */
        update:function(con,doc,success,fail){
            connect(col,function(col,db){
                col.update(con,doc,function(err,len){
                    if(err){
                        fail && fail(err);
                    }else{
                        success && success(len)
                    }
                    db.close();
                })
            })
        },
        /**
         * 查找数据
         * @param con
         * @param success
         * @param fail
         */
        find:function(con,success,fail){
            connect(col,function(col,db){
                col.find(con).toArray(function(err,docs){
                    if(err){
                        fail && fail(err)
                    }else{
                        success && success(docs);
                    }
                    db.close();
                })
            })
        }
    }
}

/**
 * 打开数据库,操作集合
 * @param col  集合明个
 * @param fn  操作方法
 */
function connect(col,fn){
    d.open(function(err,db){
        if(err){
            throw err;
        }else{
            db.collection(col,function(err,col){
                if(err){
                    throw err;
                }else{
                    fn && fn(col,db);
                }
            });
        }
    });
}

//test
var book = DB("book");
book.insert({title:"java",type:"js"})

节流模式(Throttler)

针对重复业务逻辑进行节流控制,执行最后一次操作并且取消其他的操作,提高性能。

节流器第一件事就是清除将要执行的函数,第二就是延迟执行函数。如下:

/**
  * 节流模式
  * 对于重复业务逻辑进行节流控制,执行最后一次操作,
  * 并且取消其他的操作,从而提高性能
  * 如下:
  */
//节流器
var throttle = function(){
    //获取第一个参数
    var isClear = arguments[0];
    var fn;

    if (typeof isClear === "boolean") {
        fn = arguments[1];
        fn.__throttleID && clearTimeout(fn.__throttleID);
    }else{
        fn = isClear;
        param = arguments[1];
        var  p = extend({
            context:null,
            args:[],
            time:300,
        },param);

        arguments.callee(true,fn);
        fn.__throttleID = setTimeout(() => {
            fn.apply(p.context,p.args);
        }, p.time);
    }
}

/**
 * 创建浮层类 ,用于优化浮层
 * 鼠标滑过时,显示对应的二维码图片
 */
var Layer = function(id){
    this.container = $(id);
    this.layer = $tag("div",container)[0];

    this.lis = $tag("li",this.container);
    this.imgs = $tag("img",this.container);
    //绑定事件
    this.bindEvent();
}

layer.prototype = {
    //交互事件
    bindEvent:function(){
        //缓存当前对象
        var that = this;
        function hiddeLayer(){
            that.layer.className = "";
        }
        function showLayer(){
            that.layer.className = "show";
        }

        that.on(that.container,'mouseenter',function(){
            throttle(true,hiddeLayer);
            throttle(showLayer);
        }).on(that.container,"mouseleave",function(){
            //延迟浮层隐藏
            throttle(hiddeLayer);
            //清除显示浮层方法计时器
            throttle(true,showLayer);
        });
        //绑定icon事件
        for(var i=0;i<that.lis.length;i++){
            that.lis[i].index = i;
            that.on(that.lis[i],"mouseenter",function(){
                var index = this.index;
                for(var i = 0;i<that.imgs.length;i++){
                    that.imgs[i].className = "";
                }

                that.imgs[index].className = "show";
                that.layer.style.left = -22 + 60*index+"px";
            })
        }
    },

    //事件绑定的方法
    on:function(ele,type,fn){
        ele.addEventListener ? ele.addEventListener(type,fn,false) : ele.attachEvent("on"+type,fn);
        return this;
    }
}

/**
 * 节流延迟图片加载
 * @param id  加载图片的容器
 */

function Layerload(id){
    this.container = document.getElementById(id);
    this.imgs = this.getImgs();

    this.init();
}

Layerload.prototype = {
    init:function(){
        this.update();
        this.bindEvent();
    },
    //获取加载图片
    getImgs:function(){
        var arr = [];
        var imgs = this.container.getElementsByTagName("img");
        for(var i = 0,len=imgs.length;i<len;i++){
            arr.push(imgs[i])
        }
        return arr;
    },
    //加载图片
    update:function(){
        if (!this.imgs.length) {
            return
        }

        var len = this.imgs.length;
        for(--i;i>=0;i--){
            //图片在可视范围内
            if (this.show(i)) {
                this.imgs[i].src = this.imgs[i].getAttribute('data-src');
                //清除缓存中的图片
                this.imgs.splice(i,1);
            }
        }
    },
    show:function(i){
        var img = this.imgs[i];
        var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
        var scrollBottom = scrollTop+document.documentElement.clientHeight;
        var imgTop = this.pageY(img);
        var imgBottom = imgTop + img.offsetHeight;

        if (imgBottom>scrollTop && imgBottom<scrollBottom || (imgTop > scrollTop && imgTop<scrollBottom)) {
            return true;
        }
        return false;
    },
    pageY:function(element){
        //存在父元素
        if (element.offsetParent) {
            return element.offsetTop + this.pageY(element.offsetParent);
        }else{
            return element.offsetTop
        }
    },
    on:function(element,type,fn){
        if (element.addEventListener) {
            addEventListener(type,fn,false);
        }else{
             element.attachEvent("on"+type,fn,false);
        }
    },
    bindEvent:function(){
        var that =this;
        this.on(widnow,"resize",function(){
            throttle(that.update,{context:that});
        });

        this.on(widnow,'scroll',function(){
            throttle(that.update,{context:that});
        })
    }
}


/**
 *  使用节流模式打包来优化请求次数
 */
var LogPack = function(){
    var data = [],//请求缓存数组
       MaxNum = 10, //缓存最大值
       itemSplitStr = '|',//统计参数间隔符号
       keyValueSplitStr = "*" ,//统计参数键值对间隔
       img = new Image(); //请求触发器,
    function sendLog(){
        //请求参数
        var logStr = '';
        fireData = data.splice(0,MaxNum);
        for(var i =0,len = fireData.length;i<len;i++){
            logStr += 'log'+i+"=";
            for(var j in fireData[i]){
                logStr += j + keyValueSplitStr + fireData[i][j];
                logStr += itemSplitStr;
            }
            logStr = logStr.replace(/\|$/,'') + "&";
        }
        logStr += "logLength="+len;
        img.src = "d.gif?"+logStr;
    }

    //统计的方法
    return function(param){
        //没有参数就发送统计
        if (!param) {
            sendLog();
            return;
        }
        //添加统计项
        data.push(param);
        //统计项大于请求缓存最大值就发送统计请求包
        data.length>=MaxNum && sendLog();
    }
}

//测试
btn.onclick = function(){
    LogPack({
        btnId:this.id,
        context:this.innerHTML,
        type:'click'
    });
}

简单模板模式

通过格式化字符串拼接出视图,避免创建视图时的大量节点操作。

/**
 *简单模板模式,就是字符串拼接
 网站活动主题模板实例
 */
//命名空间
var D = D || {};
//主体展示区容器
var root = document.getElementById("container");

D.templateString = function(str,data){
    return str.replace(/\{#(\w+)#\}/g,function(match,key){return typeof data[key] === undefined ? "" : data[key]});
}
//模板生成器
D.View = function(name){
    var v = {
        code:`<pre><code>{#code#}</code></pre>`,
        img:`<img src="{#src#}" alt="{#alt#}" title="{#title#}"/>`,
        part:`<div id="{#id#}" class="{#class#}">{#part#}</div>`,
        theme:[
            `<div>
                <h1>{#title#}</h1>
                {#content#}
            </div>`
        ].join('')
    }
    if(Object.prototype.toString.call(name) === "[Object Array]"){
        var tpl = "";
        for(var i=0,len=name.length;i<len;i++){
            tpl += arguments.callee(name[i]);
        }

        return tpl;
    }else{
        return v[name] ? v[name] : ('<'+name+'>{#'+name+'#></'+name+'>');
    }
}
//创建视图的方法集合
D.strategy = {
    "listPart":function(data){
       var s = document.createElement("div"),
       ul = "",
       ldata = data.data.li,
       tpl = D.view(['h2','p','ul']),

       liTpl = D.templateString(D.View("li"),{li:D.View(['strong','span'])});

       data.id && (s.id = data.id);
       for(var i =0,len = ldata.length;i<len;i++){
           if(ldata[i].em || ldata[i].span){
               ul += D.templateString(liTpl,ldata[i]);
           }
       }

       data.data.ul = ul;
       s.innerHTML = D.templateString(tpl,data.data);

       D.root.appendChild(s)
    },
    "codePart":function(){},
    "onlyTitle":function(){},
    "guide":function(){}
}
//视图入口
D.init = function(){
    //根据传输的视图类型创建视图
    this.strategy[data.type](data);
}

惰性模式

减少每执行时的重复分支判断,通过对对象重定义来屏蔽原对象中的分支判断。

//单体模式定义命名空间

var A = {};

A.on = function(dom,type,fn){
    if (document.addEventListener) {
        return function(dom,type,fn){
            document.addEventListener(type,fn,false);
        }
    }else if(document.attachEvent){
        return function(dom,type,fn){
            dom.attachEvent("on"+type,fn)
        }
    }else{
        return function(dom,type,fn){
            dom["on"+type] = fn;
        }
    }
    A.on(dom, type, fn)
};

//第二种方式
A.on = function (dom, type, fn) {
    if (document.addEventListener) {
        return function (dom, type, fn) {
            document.addEventListener(type, fn, false);
        }
    } else if (document.attachEvent) {
        return function (dom, type, fn) {
            dom.attachEvent("on" + type, fn)
        }
    } else {
        return function (dom, type, fn) {
            dom["on" + type] = fn;
        }
    }
}();
//两种方式都是让不必要的分支判断得到剥离。

/**
 * 创建XHR对象实例
 */
function createXHR(){
    if (typeof XMLHttpRequest != "undefined") {
        return new XMLHttpRequest();
    }else if (typeof ActiveXObject != "undefined") {
        if (typeof arguments.callee.activeXString != "string") {
            var versions = ["MSXML2.XMLHttp.6.0","MSXML2.XMLHttp.3.0","MSXML2.XMLHttp"],
                    i=0;
                    len = versions.length;
            for(;i<len;i++){
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex) {}
            }
        }
    }else{
        throw new Error("您的浏览器并不支持Ajax");
    }
}

//第一种优化方案
var createXHR = (function(){
    if (typeof XMLHttpRequest != "undefined") {
        return function(){
            return new XMLHttpRequest();
        }
    }else if (typeof ActiveXObject != "undefined") {
        if (typeof arguments.callee.activeXString != "string") {
            var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                i = 0;
            len = versions.length;
            for (; i < len; i++) {
                try {
                    new ActiveXObject(versions[i]);
                    arguments.callee.activeXString = versions[i];
                    break;
                } catch (ex) { }
            }
        }
    }else{
        return function(){
            throw new Error("您的浏览器并不支持Ajax")
        }
    }
})();

//第二种优化方案
function  createXHR(){
    if (typeof XMLHttpRequest !="undefined") {
        createXHR = function(){
            return new XMLHttpRequest();
        }
    }else if(typeof ActiveXObject != "undefined"){
        createXHR = function(){
            if (typeof arguments.callee.activeXString != "string") {
                var versions = ["MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp"],
                    i = 0;
                len = versions.length;
                for (; i < len; i++) {
                    try {
                        new ActiveXObject(versions[i]);
                        arguments.callee.activeXString = versions[i];
                        break;
                    } catch (ex) { }
                }
            }
        }
    }else{
        createXHR = function(){
            throw new Error("您的浏览器并不支持Ajax")
        }
    }
    return createXHR;
}

参与者模式

在特定的作用域中执行给定的函数,并且把参数原封不动的传递。例如网站的天气模块,一打开页面之后就定时从后端获取数据缓存下来,当用户打开查看天气时,立即展示天气信息。

/***
 * 参与者模式
 */
var B = {};

B.event.on = function(dom,type,fn,data){
    if (dom.addEventListener) {
        dom.addEventListener(type,function(){
            fn.call(dom,e,data);
        },false);
    }else if (dom.attachEvent) {
        dom.attachEvent("on" + type, function () {
            fn.call(dom, e, data);
        });
    }
}

//函数绑定
function bind(fn, context) {
    var Slice = Array.prototype.slice,
        args = Slice.call(arguments, 2);
    return function () {
        var addArgs = Slice.call(arguments),
            AllArgs = addArgs.concat(args);

        return fn.apply(context, AllArgs);
    }
}


//函数柯里化
function curry(fn){
    var Slice = Array.prototype.slice,
        args = Slice.call(arguments, 2);
    return function () {
        var addArgs = Slice.call(arguments),
            AllArgs = addArgs.concat(args);

        return fn.apply(context, AllArgs);
    }
}

等待者模式

对多个异步进程监听,来触发未来发生的动作。等待者模式就是解决那些不确定先后完成的异步逻辑。

/***
 * 等待者模式
 */

//等待对象
var Waiter = function(){
    var dfd = [],
        doneArr = [],
        failArr = [],
        slice = Array.prototype.slice,
        that = this;

        //监控对象
    var Primise = function(){
        //监控对象是否解决成功状态
        this.resolved = false;
        //监控对象是否解决失败状态
        this.rejected = false;
    }
    //监控对象
    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.slice(0);
            _exec(failArr)
        }
    }
    //创建监控对象
    that.Deferred = function(){
        return new Primise();
    }
    //回调执行方法
    function _exec(arr){
        var i =0,
            len = arr.length;
        for(;i<len;i++){
            try {
                arr[i]  && arr[i]();
            } catch (e) {}
        }
    }
    //监控异步方法
    that.when = function(){
        dfd = slice.call(arguments);
        var i = dfd.length;
        for(--i;i>=0;i--){
            if (!dfd || dfd[i].resolved || dfd[i].rejected || !dfd[i] instanceof Primise) {
                dfd.splice(i,1)
            }
        }
        return that;
    };
    //解决成功回调函数添加方法
    that.done = function(){
        doneArr = doneArr.concat(slice.call(arguments));
        return that;
    };
    //解决失败回调函数添加方法
    that.fail = function(){
        failArr = failArr.concat(slice.call(arguments));
        return that;
    };
}

//测试
        var waiter = new Waiter()
         var first = function(){
             var dtd = waiter.Deferred();
             setTimeout(() => {
                 console.log(11);
                 dtd.resolve()
             }, 5000);
             return  dtd;
         }();

         var second = function(){
             var dtd = waiter.Deferred();
             setTimeout(() => {
                 console.log(2222);
                 dtd.resolve();
             }, 10000);
             return dtd;
         }();

         waiter.when(first,second)
               .done(function(){
                   console.log(999)
               },function(){
                   console.log(888)
               })
               .fail(function(){
                   console.log(555)
               })

猜你喜欢

转载自blog.csdn.net/xuelian3015/article/details/89448019