Edit in JSFiddle

var SS = {

    // slots are stored as:
    // signalid: {
    //    slotid: [ connection1, connection2, ... ]
    // }
     _slots: {}
    
    // from 140byt.es: https://gist.github.com/982883
    ,_uuid: function b(a){return a?(a^Math.random()*16>>a/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,b)}
    
    ,_makeSignal: function(sigCtx, signal, slotCtx, slot){
        
        var  oSignal = sigCtx[signal]
            ,signalid = SS._uuid()
            ,slotid = slotCtx[slot].ssid
        
        // replace the original func with a wrapper that will trigger slots
        var signalWrap = function(){
            var slots;
            
            console.log('told to fire signal: ', arguments);

            oSignal.apply(sigCtx, arguments);
            
            if(SS._slots[signalid] && SS._slots[signalid][slotid]){
                slots = SS._slots[signalid][slotid];
                
                for(i = 0; i < slots.length; i++){
                    console.log(slots[i]);
                    slots[i].execute.apply(slots[i], arguments);
                }
            }
        }

        sigCtx[signal] = signalWrap;
        sigCtx[signal].ssid = signalid;
    }

    ,_connect: function(sigCtx, signal, slotCtx, slot, once){
        
        var signalid
            ,slotid
            ,con = new SS.Connection(sigCtx, signal, slotCtx, slot, once);
        
        if(!slotCtx[slot].ssid){
            slotCtx[slot].ssid = SS._uuid();
        }
        
        signalid = sigCtx[signal].ssid;
        slotid = slotCtx[slot].ssid;
        
        // no signalid means this is a fresh func
        if(!signalid){
            SS._makeSignal(sigCtx, signal, slotCtx, slot);
            signalid = sigCtx[signal].ssid;
        }

        if(!SS._slots[signalid]) {
            SS._slots[signalid] = {};
        }

        if(!SS._slots[signalid][slotid]) {
            SS._slots[signalid][slotid] = [];
        }
                 
        SS._slots[signalid][slotid].push( con );
    }

    ,connect: function(sigCtx, signal, slotCtx, slot){
        SS._connect(sigCtx, signal, slotCtx, slot, false);
    }    
    
    ,connectOnce: function(sigCtx, signal, slotCtx, slot){
        SS._connect(sigCtx, signal, slotCtx, slot, true);
    }

    // this removes ALL listeners matching signal/slot pair
    ,disconnect: function(sigCtx, signal, slotCtx, slot){
        var  signalid = sigCtx[signal].ssid
            ,slotid = slotCtx[slot].ssid
            ,i
            ,slots
            ,positions = []
        
        if(!slotid){
            throw new Error('Method is not a connected slot');
        }
        
        delete SS._slots[signalid][slotid];
        
        console.log('slots after disconnect', SS._slots[signalid]);
    }
    
    // empty a signal of all connections
    ,emptySignal: function(sigCtx, signal){
        var signalid = sigCtx[signal].ssid;
        
        delete SS._slots[signalid];  
    }
};

SS.Connection = function(sigCtx, signal, slotCtx, slot, once){
    this.sigCtx = sigCtx;
    this.signal = signal;
    this.slotCtx = slotCtx;
    this.slot = slot;
    this.once = once;

    if(typeof slotCtx[slot] !== 'function'){
        throw new Error('Slot must be a function');
    }
    
    if(typeof sigCtx[signal] !== 'function'){
        throw new Error('Signal must be a function');
    }
}

SS.Connection.prototype = {
    
    execute: function(){
        this.slotCtx[this.slot].apply(this.slotCtx, arguments);

        if(this.once === true){
            SS.disconnect(this.sigCtx, this.signal, this.slotCtx, this.slot);
        }
    }

};
        
(function(exports){
    
    function MyObj(){}
    MyObj.prototype = {
        
        // a slot
         refresh: function(data){ console.log('told to refresh', arguments); }
        
        // a signal
        ,ready: function(){ console.log('ready called', arguments); }
        
        ,doSomethingAsync: function(){
            var self = this;
            setTimeout(function(){
                self.ready('behold! i am data!', 'so am i');
                SS.disconnect(my, 'ready', myToo, 'refresh');
                self.ready('i should not trigger handlers');
                SS.connectOnce(my, 'ready', myToo, 'refresh');
                self.ready('i trigger once');
                self.ready('i trigger none');
            }, 1000);
        }    
        
    };
    
    window.MyObj = MyObj;
    
})(window);

var my = new MyObj();
var myToo = new MyObj();
my.doSomethingAsync();
SS.connect(my, 'ready', myToo, 'refresh');
SS.connect(my, 'ready', myToo, 'refresh');
console.log('end of sync...');