资讯专栏INFORMATION COLUMN

javascript设计模式(Alloy Team)

sf190404 / 2902人阅读

摘要:小明追女神的故事小明遇到了他的女神,打算送一朵鲜花来表白。刚好小明打听到他和有一个共同的好友,于是小明决定让来替自己来完成这件事。

1.单例模式

单例模式的核心:
(1)确保只有一个实例
(2)提供全局访问

用代理实现单例模式:

var ProxySingletonCreateDiv = (function(){
        var instance;
        return function( html ){
            if ( !instance ){
                instance = new CreateDiv( html );
            }
            return instance;
        }
    })();

通用的惰性单例模式:创建登陆悬浮窗

//fn保存创建逻辑
//单例模式:只创建一次
 var getSingle = function( fn ){
        var result;
        return function(){
            return result || ( result = fn .apply(this, arguments ) );
        }
    };

    var createLoginLayer = function(){
        var div = document.createElement( "div" );
        div.innerHTML = "我是登录浮窗";
        div.style.display = "none";
        document.body.appendChild( div );
        return div;
    };
    var createSingleLoginLayer = getSingle( createLoginLayer );
    document.getElementById( "loginBtn" ).onclick = function(){
        var loginLayer = createSingleLoginLayer();
        loginLayer.style.display = "block";
    };
2.发布-订阅者模式

(1)首先要指定好谁充当发布者
(2)然后给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者
(3)最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者函数

var Event = (function(){
        var clientList = {},
        listen,
        trigger,
        remove;
        listen = function( key, fn ){
            if ( !clientList[ key ] ){
                clientList[ key ] = [];
            }
            clientList[ key ].push( fn );
        };
        trigger = function(){
            var key = Array.prototype.shift.call( arguments ),
            fns = clientList[ key ];
            if ( !fns || fns.length === 0 ){
                return false;
            }
            for( var i = 0, fn; fn = fns[ i++ ]; ){
                fn.apply( this, arguments );
            }
        };
        remove = function( key, fn ){
            var fns = clientList[ key ];
            if ( !fns ){
                return false;
            }
            if ( !fn ){
                fns && ( fns.length = 0 );
            }else{
                for ( var l = fns.length - 1; l >=0; l-- ){
                    var _fn = fns[ l ];
                    if ( _fn === fn ){
                        fns.splice( l, 1 );
                    }
                }
            }
        };
        return {
            listen: listen,
            trigger: trigger,
            remove: remove
        }
    })();

给所有的对象都动态安装一个发布-订阅功能:

    var installEvent = function( obj ){
        for ( var i in event ){
            obj[ i ] = event[ i ];
        }
    };
3.装饰着模式

(1)装饰者模式可以动态的给某个对象添加一些额外的职责,而不会影响从这个类中派生出的其他对象。
(2)装饰者也是包装器:装饰者模式将一个对象嵌入到另一个对象中,实际上相当于这个对象被另一个对象包装起来,形成一条包装链。

3-1.在不改变原来函数的情况下给函数增加新的功能

var a=function(){
        alert(1);
    }
    var _a = a;
    a=function(){
        _a();
        alert(2);
    }

但是这样做会带来两个问题:必须维护_a这个中间变量;存在this被劫持的问题
3-2.用AOP装饰函数

Function.prototype.before = function( beforefn ){
        var __self = this; // 保存原函数的引用
        return function(){ // 返回包含了原函数和新函数的"代理"函数
            beforefn.apply( this, arguments ); // 执行新函数,且保证this 不被劫持,新函数接受的参数
        // 也会被原封不动地传入原函数,新函数在原函数之前执行
            return __self.apply( this, arguments ); // 执行原函数并返回原函数的执行结果,
        // 并且保证this 不被劫持
    }
}

Function.prototype.after = function( afterfn ){
    var __self = this;
    return function(){
        var ret = __self.apply( this, arguments );
        afterfn.apply( this, arguments );
        return ret;
    }
};
//调用:重写原来函数
formSubmit = formSubmit.before( validata );

4.策略模式

策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
4-1.使用策略模式计算奖金
绩效为S的人年终奖金是工资的4倍,绩效为A的人年终奖金是工资的3倍,绩效为B的人年终奖金是工资的2倍

var performanceS = function(){};
    performanceS.prototype.calculate = function( salary ){
        return salary * 4;
    };
    var performanceA = function(){};
    performanceA.prototype.calculate = function( salary ){
        return salary * 3;
    };
    var performanceB = function(){};
    performanceB.prototype.calculate = function( salary ){
        return salary * 2;
    };

    //接下来定义奖金类Bonus:

    var Bonus = function(){
        this.salary = null; // 原始工资
        this.strategy = null; // 绩效等级对应的策略对象
    };
    Bonus.prototype.setSalary = function( salary ){
        this.salary = salary; // 设置员工的原始工资
    };
    Bonus.prototype.setStrategy = function( strategy ){
        this.strategy = strategy; // 设置员工绩效等级对应的策略对象
    };
    Bonus.prototype.getBonus = function(){ // 取得奖金数额
        return this.strategy.calculate( this.salary ); // 把计算奖金的操作委托给对应的策略对象
    };

    var bonus = new Bonus();
    bonus.setSalary( 10000 );

    bonus.setStrategy( new performanceS() ); // 设置策略对象
    console.log( bonus.getBonus() ); // 输出:40000

4-2.javascript版本的策略模式

var strategies = {
        "S": function( salary ){
            return salary * 4;
        },
        "A": function( salary ){
            return salary * 3;
        },
        "B": function( salary ){
            return salary * 2;

        }
    };
    var calculateBonus = function( level, salary ){
        return strategies[ level ]( salary );
    };

    console.log( calculateBonus( "S", 20000 ) ); // 输出:80000
    console.log( calculateBonus( "A", 10000 ) ); // 输出:30000

通过使用策略模式重构代码,我们消除了原来程序中大片的条件分支代码。所有和计算奖金的逻辑不再放在context中,而是分布在各个策略对象中。每个策略对象负责的算法已被各自封装在对象内部。当我们对这些策略对象发出“计算奖金”请求时,它们会返回各自不同的计算结果。

4-3.用策略模式进行表单校验
(1)用户名不能为空
(2)密码长度不能少于6位
(3)手机号码必须符合格式



    
请输入用户名: 请输入密码: 请输入手机号码:
5.代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
5-1.小明追女神的故事
小明遇到了他的女神A,打算送一朵鲜花来表白。刚好小明打听到他和A有一个共同的好友B,于是小明决定让B来替自己来完成这件事。

var Flower = function(){};
var xiaoming = {
    sendFlower: function( target ){
        var flower = new Flower();
        target.receiveFlower( flower );
    }
};
var A = {
    receiveFlower: function( flower ){
        console.log( "收到花 " + flower );
    }
};
xiaoming.sendFlower( A );


//接下来,我们引入代理B,即小明通过B 来给A 送花:
var Flower = function(){};
var xiaoming = {
    sendFlower: function( target){
        var flower = new Flower();
        target.receiveFlower( flower );
    }
};
var B = {
    receiveFlower: function( flower ){
        A.receiveFlower( flower );
        90 第6 章 代理模式
    }
};
var A = {
    receiveFlower: function( flower ){
        console.log( "收到花 " + flower );
    }
};
xiaoming.sendFlower( B );

//然后选择A 心情好的时候把花转交给A,代码如下:

var Flower = function(){};
var xiaoming = {
    sendFlower: function( target){
        var flower = new Flower();
        target.receiveFlower( flower );
    }
};
var B = {
    receiveFlower: function( flower ){
        A.listenGoodMood(function(){ // 监听A 的好心情
            A.receiveFlower( flower );
        });
    }
};

var A = {
    receiveFlower: function( flower ){
        console.log( "收到花 " + flower );
    },
    listenGoodMood: function( fn ){
        setTimeout(function(){ // 假设10 秒之后A 的心情变好
            fn();
        }, 10000 );
    }
};

xiaoming.sendFlower( B );

var myImage = (function(){
    var imgNode = document.createElement( "img" );
    document.body.appendChild( imgNode );
    return {
        setSrc: function( src ){
            imgNode.src = src;
        }
    }
})();

5-2.保护代理与虚拟代理
保护代理:比如B可以帮助A过滤掉一些请求,如送花的人中年龄较大的
虚拟代理:new Flower可能是非常昂贵的,这时候需要B代理去执行,代理B在A心情好的时候再执行。
5-3.虚拟代理实现图片预加载

var myImage = (function(){
        var imgNode = document.createElement( "img" );
        document.body.appendChild( imgNode );
        return function( src ){
            imgNode.src = src;
        }
    })();
    var proxyImage = (function(){
        var img = new Image;
        img.onload = function(){
            myImage( this.src );
        }
        return function( src ){
            myImage( "file:// /C:/Users/svenzeng/Desktop/loading.gif" );
            img.src = src;

        }
    })();
    proxyImage( "http:// imgcache.qq.com/music// N/k/000GGDys0yA0Nk.jpg" );

纵观整个程序,我们并没有改变或者增加myImage接口,但是通过代理对象,实际上是给系统添加了新的行为:给img节点设置 src和图片预加载这两个功能。
5-4.虚拟代理合并http请求


    1
    2
    3
    4
    5
    6
    7
    8
    9

script.js:

var synchronousFile = function( id ){
        console.log( "开始同步文件,id 为: " + id );
    };

    var proxySynchronousFile = (function(){
        var cache = [], // 保存一段时间内需要同步的ID
        timer; // 定时器
        return function( id ){
            cache.push( id );
            if ( timer ){ // 保证不会覆盖已经启动的定时器
                return;
            }
            timer = setTimeout(function(){
            synchronousFile( cache.join( "," ) ); // 2 秒后向本体发送需要同步的ID 集合
            clearTimeout( timer ); // 清空定时器
            timer = null;
            cache.length = 0; // 清空ID 集合
        }, 2000 );
        }
    })();

    var checkbox = document.getElementsByTagName( "input" );
    for ( var i = 0, c; c = checkbox[ i++ ]; ){
        c.onclick = function(){
            if ( this.checked === true ){
                proxySynchronousFile( this.id );
            }
        }
    };

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82818.html

相关文章

  • 最初,唯有时光记得

    摘要:前言最初,唯有时光记得。起初,按照自己的学习习惯来,一直秉承着好记性不如烂笔头的学习理念,开始做纸质的笔记,累计了好多本之后,发现有很多的不便利,例如图片随时要改动注明来源等。微信公众号微博。 回顾 Retrospect to the past and look into the future 最近在积极地学习webview,原本打算整理一下写成一篇文章。无奈时间有限,暂时没有把握把w...

    I_Am 评论0 收藏0
  • 最初,唯有时光记得

    摘要:前言最初,唯有时光记得。起初,按照自己的学习习惯来,一直秉承着好记性不如烂笔头的学习理念,开始做纸质的笔记,累计了好多本之后,发现有很多的不便利,例如图片随时要改动注明来源等。微信公众号微博。 回顾 Retrospect to the past and look into the future 最近在积极地学习webview,原本打算整理一下写成一篇文章。无奈时间有限,暂时没有把握把w...

    lanffy 评论0 收藏0
  • 最初,唯有时光记得

    摘要:前言最初,唯有时光记得。起初,按照自己的学习习惯来,一直秉承着好记性不如烂笔头的学习理念,开始做纸质的笔记,累计了好多本之后,发现有很多的不便利,例如图片随时要改动注明来源等。微信公众号微博。 回顾 Retrospect to the past and look into the future 最近在积极地学习webview,原本打算整理一下写成一篇文章。无奈时间有限,暂时没有把握把w...

    stackvoid 评论0 收藏0
  • 为eject后的create-react-app配置ESLint

    摘要:一开始我以为是的问题,然后重新手写了一些配置依然会报错,证明不是的问题。规则真的很严格。配置问题按照的文档默认是,按照说明应该是不会阻止。 问题 项目一开始使用的是create-react-app创建的,配置的ESLint是用的AlloyTeam的eslint-config-alloy/react, 默认配置已经很合理了,并且每条配置都有相应的说明,只需要再根据个人喜好修改一些rule...

    Dean 评论0 收藏0
  • 浏览器的缓存(2)

    摘要:的兼容性由于是现代的技术,以下的古老浏览器是不支持的。当然也可以手动指定文件这些浏览器都不能直接使用缓存,即可能会要求你重新验证,或者直接使用服务器文件。 亲,如果你还在为你没网打开不网页而烦恼吗?亲,你还在为你web服务器复杂的配置项而蛋疼吗?不要998,manifest抱回家~manifest自H5横空出世以来给前端网页的浏览带来了翻天覆地的变化,以前我们的网页必须在有网的前提下打...

    impig33 评论0 收藏0

发表评论

0条评论

sf190404

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<