JavaScript设计模式

转载自:JavaScript设计模式

之前看了 JavaScript 中常见设计模式整理 这篇文章,这里我也把平时整体的设计模式分享一下。
设计模式是解决一类问题的模板,为软件设计中常见的问题提供解决方案。JavaScript 是一种弱类型、动态的、基于原型的语言,所以它可以以很简单的方式去实现一些模式。切记不要去套用后台语言的设计模式,这往往会丢失 JavaScript 这门语言的动态性和灵活性。

一些常用的设计模式

  • 单例模式:保证一个对象只有一个实例,第二次创建的实例和第一次创建的完全一样。
//单例模式
//一个类只能构造出唯一实例

//静态属性方式实现
function Person() {
    if (typeof Person.instance === 'object') {
        return Person.instance;
    }
    this.name = 'vector';
    this.age = 25;
    Person.instance = this;
}

//闭包方式实现
var Person = (function () {
   var instance;
   return function (name, age) {
       if (instance) {
           return instance;
       }
       this.name = 'vector';
       this.age = 25;
       instance = this;
   };
}());

var p1 = new Person();
var p2 = new Person();
console.log(p1 === p2);  //true

  • 工厂模式:提供创建对象的方法。

//工厂模式
//工厂模式用于生产对象

function factory(name, age) {
    var obj = {};
    obj.name = name;
    obj.age = age;
    obj.sayName = function () {
        console.log(this.name);
    };
    obj.sayAge = function () {
        console.log(this.age);
    };
    return obj;
}

var p1 = factory('vector', 25);
console.log(p1);  //{name: "vector", age: 25, sayName: ƒ, sayAge: ƒ}
  • 迭代器模式:为遍历一个数据结构提供方法。
//迭代器模式
//遍历某一个数据结构,这里以数组为例

var iterator = (function () {
    var index = 0,
        data = [1, 2, 3, 4, 5],
        len = data.length;
    return {
        next: function () {
            if (!this.hasNext()) {
                return null;
            }
            return data[index++];
        },
        hasNext: function () {
            return index < len;
        }
    };
}());

while(iterator.hasNext()) {
    console.log(iterator.next());
}
  • 装饰者模式:增强普通对象的功能,按照顺序进行装饰。
//装饰者模式
//增加普通对象的功能

function Sale(price) {
    this.price = price;
    this.decorators_list = [];
}

Sale.decorators = {};
Sale.decorators.fedtax = {
    getPrice: function (price) {
        return price + price * 5 / 100;
    }
};
Sale.decorators.money = {
    getPrice: function (price) {
        return '$' + price.toFixed(2);
    }
};

Sale.prototype.decorate = function (decorator) {
    this.decorators_list.push(decorator);  
};

Sale.prototype.getPrice = function () {
    var price = this.price,
    i,
    len = this.decorators_list.length,
    name;
    for (i = 0; i < len; i++) {
        name = this.decorators_list[i];
        price = Sale.decorators[name].getPrice(price);
    }
    return price;
};

var sale = new Sale(100);
sale.decorate('fedtax');
sale.getPrice();  //105
sale.decorate('money');
sale.getPrice();  //$105.00
  • 策略模式:根据不同命令命中不同算法,可以避免使用多重条件语句。
//策略模式
//根据不同命令可以命中不同的算法

var operate = {
    add: function (a, b) {
        return a + b;
    },
    sub: function (a, b) {
        return a - b;
    },
    mul: function (a, b) {
        return a * b;
    },
    div: function (a, b) {
        return a / b;
    }
};

var calc = function (cmd, arg1, arg2) {
    return operate[cmd](arg1, arg2);
};

calc('add', 1, 3);  //4
calc('mul', 2, 4);  //8

  • 外观模式:将复杂的子系统功能隐藏在外观之后,提供简单的调用接口。

//外观模式
//为复杂的子系统功能提供简单的调用接口
//在重构工作和写兼容代码中很有帮助

var myevent = {
    //阻止冒泡和默认事件
    stop: function (e) {
        if (typeof e.preventDefault === 'function') {
            e.preventDefault();
        }
        if (typeof e.stopPropagation === 'function') {
            e.stopPropagation();
        }
        //IE
        if (typeof e.returnValue === 'boolean') {
            e.returnValue = false;
        }
        if (typeof e.cancelBubble === 'boolean') {
            e.cancelBubble = true;
        }
    }
};

  • 代理模式:通过包装一个对象以控制对它的访问,ES6中的Proxy就是通过代理扩展对象功能。
//代理模式
//通过包装一个对象以控制对它的访问

//以缓存代理为例
const requestResult = async function (id) {
    let requestConfig = {
        credentials: 'include',
        method: 'GET',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        mode: "cors",
        cache: "force-cache"
    };
    let url = '/service/v1/' + id;
    const response = await fetch(url, requestConfig);
    const responseJson = await response.json();
    return responseJson;
};

const proxy = {
    cache: {},
    request: async function (id) {
        if (this.cache[id]) {
            //直接取缓存内容
            return this.cache[id];
        } else {
            return requestResult(id);
        }
    }
};

proxy.request(1);
proxy.request(1);  //从缓存获取

  • 中介者模式:对象之间不直接通信,借助中介对象进行通信,解决对象间通信产生的紧耦合问题。
//中介者模式
//独立的对象之间不直接通信,借助中介对象,当其中一个独立对象状态改变后,通知中介对象,中介对象也会将该变化通知到其他应该知道此变化的对象

//以两个玩家玩游戏为例,玩家play1按按键1,玩家play2按按键2,30s内看谁按得次数最多,并且分数实时显示在计分板

//玩家构造函数
function Player (name) {
    this.points = 0;
    this.name = name;
}
Player.prototype.play = function () {
    this.points++;
    mediator.played();
};

//计分板对象
var scoreboard = {
    element: document.getElementById('result'),
    //更新得分显示
    update: function (score) {
        var i, msg = '';
        for (i in score) {
            if (score.hasOwnProperty(i)) {
                msg += '<p><strong>' + i + '<\/strong>: ';
                msg += score[i];
                msg += '<\/p>';
            }
        }
        this.element.innerHTML = msg;
    }
}

//中介对象,作为play1、play2、计分板、按键事件之间通信中介
var mediator = {
    //所有的玩家
    players: {},

    //初始化
    setup: function () {
        var players = this.players;
        players.play1 = new Player('play1');
        players.play2 = new Player('play2');
    },

    //如果有人played,则更新分值
    played: function () {
        var players = this.players;
        var score = {
            play1: players.play1.points,
            play2: players.play2.points
        };
        scoreboard.update(score);
    },

    //处理用户交互
    keypress: function (e) {
        e = e || window.event;
        if (e.which == 49) {
            //按键1
            mediator.players.play1.play();
            return;
        } else if (e.which == 50) {
            //按键2
            mediator.players.play2.play();
            return;
        }
    }
};

//运行游戏
mediator.setup();
window.onkeypress = mediator.keypress;
setTimeout(function () {
    window.keypress = null;
    alert('game over!');
}, 30000);


  • 观察者模式:又称发布-订阅模式,发布者中保存着一份订阅者的列表,当发布者的状态发生改变的时候就主动向订阅者发出通知。
//观察者模式
//当发生一个感兴趣的事件时,将该事件通告给所有观察者
//形成对象之间的松散耦合

function EventTarget() {
    this.handlers = {};
}
EventTarget.prototype = {
    constructor: EventTarget,
    addHandler: function (type, handler) {
        if (typeof this.handlers[type] == "undefined") {
            this.handlers[type] = [];
        }
        this.handlers[type].push(handler);
    },
    fire: function (event) {
        if (!event.target) {
            event.target = this;
        }
        if (this.handlers[event.type] instanceof Array) {
            var handlers = this.handlers[event.type];
            for (var i = 0, len = handlers.length; i < len; i++) {
                handlers[i](event);
            }
        }
    },
    removeHandler: function (type, handler) {
        if (this.handlers[type] instanceof Array) {
            var handlers = this.handlers[type];
            for (var i = 0, len = handlers.length; i < len; i++) {
                if (handlers[i] === handler) {
                    break;
                }
            }
            handlers.splice(i, 1);
        }
    }
};

var publisher = new EventTarget();  //定义发布者
//向发布者中注册订阅者
publisher.addHandler('call1',function () {
    console.log('call1');
});
publisher.addHandler('call1',function () {
    console.log('call1 again');
});
//发布者发布消息
publisher.fire({type: 'call1'});  //call1 call1 again

  • 事件委托模式:在定义DOM事件时用的最多,在父元素上监听事件代替给众多子元素监听事件。
//事件委托模式
//给每一个子元素注册监听器比较耗内存,基于DOM事件冒泡的原理,将事件监听器注册在父元素上。

//给ul下的li绑定点击事件
var ul = document.getElementById('list');
ul.addEventListener('click', function (e) {
    var target = e.target;
    if (target.node === 'LI') {
        //处理逻辑
    }
});


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值