Javascript模式(二) 发布者/订阅者模式

var publisher = {

    // 订阅者数组
    subscribers  : {
        "any" : []
    },

    // 增加订阅者
    on : function(type, fn, context){
        var subscribers = this.subscribers;
        type = type || "any";
        context = context || this;
        fn = typeof fn === "function" ? fn : context[fn];
        if(!subscribers[type]){
            subscribers[type] = [];
        }
        subscribers[type].push({"fn" : fn, "context" : context});
    },

    // 移除订阅者
    off : function(type, fn, context){
        this.visit("unPublish", type, fn, context)
    },

    // 通知
    fire : function(type, arg){
        this.visit("publish", type, arg);
    },

    // 访问订阅者数组
    visit : function(action, type, arg, context){
        var subscribers, i, len;
        type = type || "any";
        subscribers = this.subscribers[type];
        len = subscribers.length || 0;

        switch(action){
            case "publish" :
                for(i = 0; i < len; i++){
                    subscribers[i].fn.call(subscribers[i].context, arg);
                }
                break;
            case "unPublish":
                for(i = 0; i < len; i++){
                    if(subscribers[i].fn === arg && subscribers[i].context === context){
                        subscribers.splice(i, 1);
                    }
                }
                break;
        }
    }
};

function makePublisher(o){
    o.subscribers = {"any" : []};
    for(var p in publisher ){
        if(publisher.hasOwnProperty(p) && typeof publisher[p] === "function"){
            o[p] = publisher[p];
        }
    }
    return o;
}

// 发布者Play
// 发布两个事件:1、有玩家加入 2、玩家开始玩
function Player(name, key){
    this.point = 0;
    this.name = name;
    this.key = key;
    this.fire("add", this);
}

Player.prototype.play = function(){
    this.point += 1;
    this.fire("play", this);
};

// 观察者/订阅者 game,观察2个事件:1、有玩家加入 2、玩家开始玩
// 同时作为发布者game,通知积分榜更新  
var game = {
    // 存储对象和按键key的关系
    keys : {},
    // 订阅
    addPlayer : function(player){
        this.keys[player.key] = player;
    },

    // 通知订阅者scoreboard更新
    handlyPlay : function(){
        var score = {}, keys = this.keys, p;
        for(p in keys){
            if(keys.hasOwnProperty(p)){
                score[keys[p].name] = keys[p].point;
            }
        }
        this.fire("update", score);
    },

    // 封装keypress事件
    keydown : function(e){
        var which, code;
        e = e || event;
        which = e.which || e.keyCode;
        code = String.fromCharCode(which);
        if(this.keys[code]){
            this.keys[code].play();
        }
    }
};

// 积分榜
var scoreboard = {
    dom : document.getElementById("score_board"),

    // 更新积分榜 参数格式 {playname1 : point, playname2 :  point }
    update : function(score){
        var p, html = "";
        for(p in score){
            if(score.hasOwnProperty(p)){
                html += p + "获得了" + score[p] + "<br/>";
            }
        }
        this.dom.innerHTML = html;
    }
};

// Player作为发布者,因其需要通知订阅者game新增玩家以及玩家积分变化
// game对Player而言是订阅者,因其需要订阅Player的特定活动add(新增玩家)和play(玩家积分发生变化)
// game对scoreboard而言是发布者,因其在观察到Player的play事件之后需要通知scoreboard更新积分
makePublisher(Player.prototype);
makePublisher(game);

Player.prototype.on("add", game.addPlayer, game);
Player.prototype.on("play", game.handlyPlay, game);
game.on("update", scoreboard.update, scoreboard);


//excute
while(true){
    var name = prompt("say your name, man"), key;
    if(name && name !== "null"){
        while(true){
            key = prompt("what is your key");
            if(key && key !== "null"){
                break;
            }
            alert("亲,还是指定个key吧,不然你没办法玩的,相信我");
        }
        new Player(name, key);
    }
    else {
        break;
    }
}

document.onkeydown = function(e){
    game.keydown.call(game, e);
};
原文地址:https://www.cnblogs.com/mr189/p/3954068.html