资讯专栏INFORMATION COLUMN

常用js设计模式整理

Ocean / 2071人阅读

摘要:架构没预先设计好,强耦合性代码在后期维护简直灾难。大型单页应用里,复杂度上升到一定程度时,没有适当的设计模式进行降耦,后续的开发也难以下手。而设计模式正是为了降耦而存在。面向对象设计鼓励将行为分布到细粒度的对象之中。

在做canvas、webGL游戏时,很深切的感觉到,游戏编程玩的都是设计模式。架构没预先设计好,强耦合性代码在后期维护简直灾难。

大型单页应用里,复杂度上升到一定程度时,没有适当的设计模式进行降耦,后续的开发也难以下手。

而设计模式正是为了降耦而存在。

参考《javascript设计模式》——曾探

函数节流
var throttle = function(fn, interval){
    var _self = fn,
        timer,
        firstTime = true;

    return function(){
        var args = arguments.
            _me = this;
        
        if( firstTime ){
                _self.apply(_me, args);
            return first
            }
            
            if(timer){
            return false;
        }
    
        timer = setTimeout(function(){
            clearTimeout(timer);
            timer = null;
            _self.apply(_me, args);
        }, interval || 500);
    }    
}

window.onresize = throttle(function(){
    console.log(1);
}, 500);
分式函数
var timeChunk = function(arr, fn, count){
    var obj, t;
    var len = ary.length;

    var start = function(){
        for(var i = 0; i < Math.min(coutn||1, arr.length); i++){
            var obj = ary.shift();
            fn(obj);
        }
    }

    return function(){
        t = setInterval(function(){
            if(ary.length === 0){
                return clearInterval(t);
            }
            start();
      
        }, 200);
    };
};
惰性函数(重写自重写)
var addEvent = function(elem, type, handler){
if(window.addEventListener){
    addEvent = function(elem, type, handler){
        elem.addEventListener(type, handler, false);
    }
} else if(window.addachEvent){
    addEvent = function(elem, type, handler){
        elem.attachEvent("on" + type, handler);   
    }
}

}

单例模式

定义:保证类有且仅有一个实力,并提供全局访问接口。

私有变量加下划线 var _a = 1;
通用惰性单例模式

把创建单例和管理单例的职责分开来:

var getSingle = function(fn){
    var result;
    return function(){
        return result || (result = fn.apply(this, arguments));
    }
}

var createLayer = function(){
    var div = document.createElement("div");
    document.body.appendChild(div);
}

var createSingleCreateLayer = getSingle(createLayer);

document.getElementById("#test").onclick = () => {
    createSingleCreateLayer();
}


策略模式

定义:封装一系列算法,并使其可以相互替换

计算工资Demo:

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

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

表单添加多种校验规则:

/*--------------- Strategies --------------*/
var strategies = {
    isNotEmpth: (value, errorMsg) => {
        if(value == ""){
            return errorMsg;
        }
    },
    minLength: (value, length,errorMsg) => {
        if(value.length < length){
            return errorMsg;
        }
    }
}

/*---------------- Validator ---------------*/
var Validator = () => {
    this.cache = [];
}

Validator.prototype.add = (dom, rules) => {
    var self = this;
    
    for(var i = 0; rule; rule = rules[i++]){
        ((rule) => {
            var strategyAry = strategy.split(":");
            var errorMsg = rule.errorMsg;
            
            self.cache.push(function(){
                var strategy = strategyAry.shift();
                strategyAry.unshift(dom.value);
                strategyAry.push(errorMsg);
                
                return strategies[strategy].apply(dom, strategyAry);
            });
        })(rule);
    }
};

Validator.prototype.start = () => {
    for(var i = 0; validatorFunc; validatorFunc = this.cache[i++]){
        var errorMsg = validatorFunc();
        
        if( errorMsg ){
            return errorMsg;
        }
    }
}

/*-------------- 客户端调用 ------------*/
var registerForm = document.getElementById("registerForm");

var validataFunc = () => {
    var validator = new Validator();
    
    validator.add(registorForm.username, [{
        strategy: "isNotEmpty",
        errorMsg: "用户名不能为空"
    },{
        strategy: "minLength",
        errorMsg: ‘最下长度为6’
    }]);
    
    validator.add(registorForm.password, [{
        strategy: "minLength",
        errorMsg: "最小长度"
    }])
    
    var errorMsg = validator.start();
    return errorMsg;
}

registerForm.onsubmit = () => {
    var errorMsg = validataFunc();
    
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}
代理模式

为一个对象提供一个代用品或者占位符,以便控制它的访问

面向对象设计原则——单一职责原则。面向对象设计鼓励将行为分布到细粒度的对象之中。

大量计算时加入代理作为缓存,异步请求时同理可用缓存:

var mult = () => {
    var a = 1;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a * arguments[i];
    }
    return a;
}

var proxyMult = (() => {
    var cache = {};
    return () => {
        var args = Array.prototype.join.call(arguments, ",");
        if( args in cache ){
            return cache[args];
        }
        
        return cache[args] = mult.apply(this, arguments);
    }
})();
高阶函数动态创建代理
var mult = () => {
    var a = 1;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a * arguments[i];
    }
    return a;
}

var plus = () => {
    var a = 0;
    for(var i = 0, l = arguments.length; i < l; i++){
        a = a + arguments[i];
    }
    return a;
}

var createProxyFactory = (fn) => {
    var cache = {};
    return () => {
        var args = Array.prototype.join.call(arguments, ",");
        if(args in cache){
            return cache[args];
        }
        
        return cache[args] = fn.apply(this, arguments);
    }
}
迭代器模式

迭代器模式指提供一种方法顺序访问一个聚合对象的各个元素,又不暴露该对象的内部表示。

通用外部迭代器:

var Iterator = (obj) => {
    var current = 0;
    
    var next = () => {
        ++current;
    }
    
    var isDone = () => {
        return current >= obj.length;
    }
    
    var getCurrItem = () => {
        return obj[current];
    }
    
    return {
        next: next,
        isDone: isDone,
        getCurrItem: getCurrItem
    }
}

发布-订阅模式
var event = {
    clientListh: [],
    listen: function(key, fn){
        if(!this.clientListh[key]){
            this.clientList[key] = [];
        }
        this.clientList[key].push(fn);
    },
    trigger: function(){
        var key = Array.prototype.shift.call(arguments),
            fns = this.clientList[key];
            
        if(!fns || fns.length === 0){
            return false;
        }
        
        for(var i = 0; fn; fn = fns[i++]){
            fn.apply(this.arguments);
        }
    }
}

var installEvent = function(obj){
    for(var i in event){
        obj[i] = event[i];
    }
};

var salesOffices = {};
installEvnet(salesOffices);

salesOffices.listen("squareMeter88", function(price){
    console.log("price": + price);
})

salesOfffices.trigger("squareMeter88", 20000);

发布——订阅模式可以很大程度降低耦合性,但滥用也会造成背后逻辑混乱,且浪费内存。

命令模式

主要是回调函数的的面向对象的替代品

没看懂命令模式有什么意义

撤销和重做
var commandStack = [];
document.onkeypress = (ev)=>{
    var keyCode = ev.keyCode,
        command = makeCommand(Ryu, commands[keyCode]);
        
    if(command){
        command();
        commandStack.push(command);
    }
}

$("#replay").onclick = ()=>{
    var command;
    while(command = commandStack.shift()){
        command();
    }
}
宏命令
var closeDoorCommand = {
    excute: ()=> {
        console.lg("close door");
    }
}

var openPcCommand = {
    excute: ()=> {
        console.log("open pc");
    }
}

var MacroCommand = ()=> {
    return {
        conmandList: [],
        add: (command)=>{
            this.commandList.push(command);
        },
        execute: ()=>{
            for(var i = 0, command; command = this.commandsList[i++]; ){
                command.execute();
            }
        }
    }
}
组合模式

类似命令模式的加强版,也是看不懂深层意义

遍历文件夹

/************ Folder ****************/
var Folder = (name)=>{
    this.name = name;
    this.parent = null;
    this.files = [];
};

Folder.prototype.add = (file)=> {
    file.parent = this;
    this.files.push(file);
}

Folder.prototype.scan = ()=>{
    console.log("Begin scan: " + this.name);
    for(var i = 0, file = this.files; file = files[i++]; ){
        file.scan();
    }
}

Folder.prototype.remove = ()=>{
    if(!this.parent){
        return;
    }
    
    for(var files = this.parent.files, l = files.length-1; l >= 0; l--){
        var file = files[l];
        if(file === this){
            files.splice(l, 1);
        }
    }
}



/************ File ****************/
var File = (name)=>{
    this.name = name;
    this.parent = null;
}

File.prototype.add = ()=>{
    throw new Error("Can not add file under file");
}

File.prototype.scan = ()=>{
    console.log("Begin Scan: " + this.name);
}

File.prototype.remove = ()=>{
    if(!this.parent){
        return;
    }
    for(var files = this.parent.files, l = files.length-1; l >= 0; l--){
        var file = files[l];
        if(file == this);{
            file.splice(l, 1);
        }
    }
}
模板方法模式

基于继承的设计模式

模板方法模式友抽象父类和具体实现的子类组成。父类封装了子类的算法框架,子类通过集成抽象类,也继承了整个算法框架。

钩子方法
var Bevrage = function(){}

Beverage.prototype.boilWater = function(){
    console.log("把水煮沸");
}
Beverage.prototype.brew = function(){
    throw new Error("Class brew musth be rewrited");
}

....

Beverage.prototype.addCondiments = function(){
    throw new Error("adCondiments must be rewrited");
}

Beverage.prototype.customerWantsCondiments = function(){
    return true;
}

Beverage.prototype.init = function(){
    this.boilWater();
    this.brew();
    ....
    if(this.customerWantsCondiments()){
        this.addCondiments();
    }    
}

var CoffeeWithHook = function(){};

CoffeeWithHook.prototype = new Beverage();

CoffeeWithHook.prototype.brew = function(){
    console.log("brew coffee with water");
};

CoffeeWithHook.prototype.customerWantsCondiments = function(){
    return widow.confirm("需要调料嘛?");
}

var coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.init();

高阶函数可以更方便的实现上面的demo...

享元模式

用于性能优化,核心是运用共享技术来支持大量细粒度的对象。

通用对象池的实现

var objectPoolFactory = (createObjFn)=>{
    var objectPool = [];
    
    return{
        create: function(){
            var obj = objectPool.length === 0 ? createObjFn.apply(this, arguments) : objectPool.shift();
            
            return obj;
        },
        recover: (obj){
            objectPool.push(obj);
        }
    }
}
职责链模式

用来重构代码挺方便的。

把不同功能函数包装成链式节点再调用。

var order500 = function(orderType, pay, stock){
    if(orderType == 1 && pay == true){
        conosle.log("500, 100优惠券")
    } else{
        return "nextSuccessor"; // 把请求往后传递
    }    
}

var order200 = function(orderTYPE, pay, stock){
    if(orderType == 2 && pay ==true){
        console.log("200, 50优惠券");
    } else{
        return "nextSuccessor";
    }
}

var orderNormal = function(orderType, pay, stock){
    if(stock > 0){
        console.log("普通购买,无优惠券");
    } else{
        console.log("库存不足");
    }
}

//职责链包装函数
//Chain.prototype.setNextSuccessor 指定在链中的下一个结点
//Chain.prototype.passRequest 请求传递给某个结点

var Chain = function(fn){
    this.fn = fn;
    this.successor = null;
}

Chain.prototype.setNextSuccessor = function(successor){
    return this.successor = seccussor;
}

Chain.prototype.passRequest = function(){
    var ret = this.fn.apply(this, arguments);
    
    if(ret === "nextSuccessor"){
        return this.successor && this.seccessor.passRequest.apply(this.successor, arguments);
    }
    
    return ret;
}

//将订单函数包装进职责链

var chainOrder500 = new Chain(order500);
var chainOrder200 = new Chain(order200);
var chainOrderNormal = new Chain(orderNormal);

chainOrder500.setNextSuccessor(chainOrder200);
ChainOrder200.setNextSuccessor(chainOrderNormal);

//Test]
chainOrder500.pasRequest(1, true, 500);


中介者模式

个人感觉有点像代理模式.用一个中介对象,来处理其他对象的时间,以实现解耦的目的。

但缺点也很明显,当系统复杂到一定程度时,中介者对象慢慢会变成一个难以维护的对象

装饰者模式

动态的给类添加职责

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

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

相关文章

  • 前端面试知识点目录整理

    摘要:写在前面金三银四又到了一年一度的跳槽季相信大家都在准备自己面试笔记我也针对自己工作中所掌握或了解的一些东西做了一个目录总结方便自己复习详细内容会在之后一一对应地补充上去有些在我的个人主页笔记中也有相关记录这里暂且放一个我的面试知识点目录大家 写在前面: 金三银四, 又到了一年一度的跳槽季, 相信大家都在准备自己面试笔记, 我也针对自己工作中所掌握或了解的一些东西做了一个目录总结,方便自...

    xzavier 评论0 收藏0
  • 前端面试知识点目录整理

    摘要:写在前面金三银四又到了一年一度的跳槽季相信大家都在准备自己面试笔记我也针对自己工作中所掌握或了解的一些东西做了一个目录总结方便自己复习详细内容会在之后一一对应地补充上去有些在我的个人主页笔记中也有相关记录这里暂且放一个我的面试知识点目录大家 写在前面: 金三银四, 又到了一年一度的跳槽季, 相信大家都在准备自己面试笔记, 我也针对自己工作中所掌握或了解的一些东西做了一个目录总结,方便自...

    enda 评论0 收藏0
  • 前端面试整理

    摘要:新布局基本数据类型,几种种也是返回类型非负区别创建对象的方式闭包的理解原型链原理手写判断是一个数组深拷贝原生操作创建元素删除元素你觉得有哪些好处还用过什么工具库事件委托事件理解规范怎么写插件怎么给数组原型添加方法怎么合并两个对象常 h5 html5 新api storage geolocation history webworker indexDB websocket can...

    yvonne 评论0 收藏0
  • 前端面试整理

    摘要:新布局基本数据类型,几种种也是返回类型非负区别创建对象的方式闭包的理解原型链原理手写判断是一个数组深拷贝原生操作创建元素删除元素你觉得有哪些好处还用过什么工具库事件委托事件理解规范怎么写插件怎么给数组原型添加方法怎么合并两个对象常 h5 html5 新api storage geolocation history webworker indexDB websocket can...

    Caizhenhao 评论0 收藏0
  • js基础常用知识点由浅入深整理

    摘要:因为同一时间,只能处理一个异步,这又牵扯到单线程问题了。然后控制台默默打印了个目前前端,异步主要为前后端交互以及定时器,仅仅说前端,如果说的话,还有文件读取等其他的方面会异步。 此篇文章完全按照我个人理解去写。 1.何为JS 先说说js干啥的。不负责点说,js就是操作浏览器的。 有人可能说nodeJS,nodeJS严格意义上只能说是用的ES,因为他没有dom ,也没有bom。 简单点说...

    Zack 评论0 收藏0
  • 正则表达式

    摘要:本文内容共正则表达式火拼系列正则表达式回溯法原理学习正则表达式,是需要懂点儿匹配原理的。正则表达式迷你书问世了让帮你生成和解析参数字符串最全正则表达式总结验证号手机号中文邮编身份证地址等是正则表达式的缩写,作用是对字符串执行模式匹配。 JS 的正则表达式 正则表达式 一种几乎可以在所有的程序设计语言里和所有的计算机平台上使用的文字处理工具。它可以用来查找特定的信息(搜索),也可以用来查...

    bang590 评论0 收藏0

发表评论

0条评论

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