js观察者模式

js观察者模式:

var publisher = {
    subscribers:{
        any:[]
    },
    subscribe:function(fn,type){
        type = type || 'any';
        if(typeof this.subscribers[type] === 'undefined'){
            this.subscribers[type] = [];
        }
        this.subscribers[type].push(fn);
    },
    unsubscribe:function(fn,type){
        this.visitSubscribers('unsubscribe',fn,type);
    },
    publish:function(publication,type){
        this.visitSubscribers('publish',publication,type);
    },
    visitSubscribers:function(action,arg,type){
        var pubtype = type || 'any',
            subscribers = this.subscribers[pubtype],
            i,
            max = subscribers.length;
        for(i=0;i<max;i+=1){
            if(action === 'publish'){
                subscribers[i](arg);
            }else{
                if(subscribers[i]===arg){
                    subscribers.splice(i,1);
                }
            }
        }
    }
};
function makePublisher(o){
    var i;
    for(i in publisher){
        if(publisher.hasOwnProperty(i) && typeof publisher[i] === 'function'){
            o[i] = publisher[i];
        }
    }
    o.subscribers = {any:[]};
}
var paper = {
    daily:function(){
        this.publish('big news today');
    },
    monthly:function(){
        this.publish('interesting analysis','monthly');
    }
};
makePublisher(paper);
var joe = {
    drinkCoffee:function(paper){
        console.log('Just read' + paper);
    },
    sundayPreNap:function(monthly){
        console.log('About to fall asleep reading this ' + monthly);
    }
};
paper.daily();
paper.daily();
paper.daily();
paper.monthly();
makePublisher(joe);
joe.tweet = function(msg){
    this.publish(msg);
};
paper.readTweets = function(tweet){
    alert('Call big meeting! Someone ' + tweet);
};
joe.subscribe(paper.readTweets);
joe.tweet('hated the paper today');

var publisher = {
    subscribers:{
        any:[]
    },
    on:function(type,fn,context){
        type = type || 'any';
        fn = typeof fn === 'function' ? fn : context[fn];
        if(typeof this.subscribers[type] === 'undefined'){
            this.subscribers[type] = [];
        }
        this.subscribers[type].push({
            fn:fn,
            context:context || this
        })
    },
    remove:function(type,fn,context){
        this.visitSubscribers('unsubscribe',type,fn,context);
    },
    fire:function(type,publication){
        this.visitSubscribers('publish',type,publication);
    },
    visitSubscribers:function(action,type,arg,context){
        var pubtype = type || 'any',
            subscribers = this.subscribers[pubtype],
            i,
            max=subscribers ? subscribers.length : 0;
        for(i=0;i<max;i+=1){
            if(action === 'publish'){
                subscribers[i].fn.call(subscribers[i].context,arg);
            }else{
                if(subscribers[i].fn === arg && subscribers[i].context === context){
                    subscribers.splice(i,1);
                }
            }
        }
    }
};
function Player(name,key){
    this.points = 0;
    this.name = name;
    this.key = key;
    this.fire('newplayer',this);
}
Player.prototype.play = function(){
    this.points += 1;
    this.fire('play',this);
};
var game = {
    keys:{},
    addPlayer:function(player){
        var key = player.key.toString().charCodeAt(0);
        this.keys[key] = player;
    },
    handleKeypress:function(e){
        e = e || window.event;
        if(game.keys[e.which]){
            game.keys[e.which].play();
        }
    },
    handlePlay:function(player){
        var i,
            players = this.keys,
            score = {};
        for(i in player){
            if(players.hasOwnProperty(i)){
                score[players[i].name] = players[i].points;
            }
            this.fire('scorechange',score);
        }
    }
};
makePublisher(Player.prototype);
makePublisher(game);
Player.prototype.on('newplayer','addPlayer',game);
Player.prototype.on('play','handlePlay',game);
game.on('scorechange',scoreboard.update,scoreboard);
window.onkeypress = game.handleKeypress;
var playername,key;
while(1){
    playername = prompt('Add player (name)');
    if(!playername){
        break;
    }
    while(1){
        key = prompt('Key for ' + playername + '?');
        if(key){
            break;
        }
    }
    new Player(playername,key);
}