Understanding the Best OOP Javascript Architecture

As I read some examples of the Angularjs UI add-on, I came across some code that showed me that my Javascript knowledge is quite effective:

The following is a class inside an Angular provider:

function Dialog(opts) { var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts); this._open = false; this.backdropEl = createElement(options.backdropClass); if(options.backdropFade){ // ... } this.handleLocationChange = function() { self.close(); }; // more functions } 

Pretty simple. But outside this class there are prototype functions, for example, the above close()

 Dialog.prototype.open = function(templateUrl, controller){ var self = this, options = this.options; // .. some code }; 

Now I do not understand why this function is declared as a prototype, but handleLocationChange inside the class itself.

How can I decide which method to choose?

Full text can be found here.

+4
source share
3 answers

Consider these two cases:

 Dialog.prototype.open = function... Dialog.open = function.... 

The first case - every object created by calling new Dialog() will have this function open

The second case has nothing to do with the objects of the dialogue, consider it a static function.

EDIT

found a great answer here: javascript-class-method-vs-class-prototype-method

+3
source

the open function will be shared by all objects creating with the new Dialog () .. and handleLocationChange will be different for different objects.

+2
source

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.

+2
source

All Articles