I think that handleLocationChange is called from an event trigger object that registers listeners but does not register the this context, so when it fires, you cannot use this since it refers to handleLocationChange. To overcome this, they decided to set the closure reference (= self variable) and call other instance functions using self. It basically stores the value known at creation, but unknown at execution of handleLocationChange.
Here is the code that shows the problem:
var eventSystem={ events:{}, add:function(eventname,fnCallback){ if(!this.events[eventname]){ this.events[eventname]=[]; } this.events[eventname].push(fnCallback); }, trigger:function(eventname){ if(!this.events[eventname]){ return; } var i=0; for(i=0;i<this.events[eventname].length;i++){ this.events[eventname][i](); } } }; var person=function(name){ this.name=name; }; person.prototype.sayName=function(){ console.log("this is now:",this.toString()); // logs this is now: function (){ console.log("this is now:... // so this is now the sayName function not the person instance console.log(this.name);//undefined: sayName doesn't have a name property } var jon=new person("jon"); eventSystem.add("sayname",jon.sayName);//add event and listener function eventSystem.trigger("sayname");//trigger the event
This is how he decided to set the link to the closure
var eventSystem={ events:{}, add:function(eventname,fnCallback){ if(!this.events[eventname]){ this.events[eventname]=[]; } this.events[eventname].push(fnCallback); }, trigger:function(eventname){ if(!this.events[eventname]){ return; } var i=0; for(i=0;i<this.events[eventname].length;i++){ this.events[eventname][i](); } } }; var person=function(name){ var self=this;// set closure ref to this this.name=name; this.sayName=function(){ console.log(self.name);//use closure ref to get this // logs jon } }; var jon=new person("jon"); eventSystem.add("sayname",jon.sayName);//add event and listener function eventSystem.trigger("sayname");//trigger the event
The following is a fix for the event system to take care of this context:
var eventSystem={ events:{}, add:function(eventname,fnCallback,thisRef){ if(!this.events[eventname]){ this.events[eventname]=[]; } this.events[eventname].push({ "callback":fnCallback,//store the event handler "thisRef":thisRef//store the this context }); }, trigger:function(eventname){ if(!this.events[eventname]){ return; } var i=0; for(i=0;i<this.events[eventname].length;i++){ this.events[eventname][i].callback.call( this.events[eventname][i].thisRef); } } }; var person=function(name){ this.name=name; }; person.prototype.sayName=function(){ console.log("this is now:",this);//referring to person instance // with the name jon console.log(this.name);//logs jon console.log(this instanceof person);//true } var jon=new person("jon"); eventSystem.add("sayname",jon.sayName,jon);//add extra parameter for this ref eventSystem.trigger("sayname");//trigger the event
The template used above is not an event system (think that it is a spray subscriber), since an event is usually triggered or called from an object (button, enter, dialog), but in the case of a larger event system, such as an implementation, it is easy to get the correct context this because you are triggering an event in or out of the instance (e.g. myButton or myDialog).
See the following code for the event system, for example:
var eventSystem={ add:function(eventname,fnCallback){ if(!this.events[eventname]){ this.events[eventname]=[]; } this.events[eventname].push(fnCallback); }, //change in trigger as it passing the event object now trigger:function(event){ if(!this.events[event.type]){ return; } var i=0; for(i=0;i<this.events[event.type].length;i++){ this.events[event.type][i](event); } }, initES:function(){//set the instance variables needed this.events=this.events||{}; } }; function addProtos(o,protos){ for(item in protos){ o.prototype[item]=protos[item]; } } var person=function(name){ this.name=name; this.initES();//needed to initialeze eventsystem }; // make person capable of storing event handlers // and triggering them addProtos(person,eventSystem); person.prototype.askQuestion=function(){ //asking a question will trigger an "answer" event this.trigger({type:"answer",target:this}); } // handler for when jon will fire an answer event function answerHandler(event){ console.log("answer from:",event.target); console.log("name of the person:",event.target.name); } var jon=new person("jon"); jon.add("answer",answerHandler);//add event listener jon.askQuestion();//triggers the answer event from within jon jon.trigger({type:"answer",target:jon});//trigger the event externally
I donβt know why Angular choose to βbreakβ the prototype using closure, as the examples show that there are other alternatives. Maybe someone can explain who is more familiar with Angular.