更好地理解Javascript OOP体系结构
在阅读Angularjs的UI插件的一些示例时,我偶然发现了一些代码,这些代码表明我对Javascript的了解是可以改进的: 以下是角度提供程序内部的类:更好地理解Javascript OOP体系结构,javascript,oop,architecture,prototype,Javascript,Oop,Architecture,Prototype,在阅读Angularjs的UI插件的一些示例时,我偶然发现了一些代码,这些代码表明我对Javascript的了解是可以改进的: 以下是角度提供程序内部的类: function Dialog(opts) { var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts); this._open = false;
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
}
很简单。但在该类之外,还有原型函数,例如上面调用的close()
现在我不明白为什么该函数被声明为一个原型,而是类本身内部的handleLocationChange
我如何决定选择哪种方法
可以找到完整的要点函数open将由使用new Dialog()创建的所有对象共享。。对于不同的对象,手动位置更改也会有所不同。考虑以下两种情况:
Dialog.prototype.open = function...
Dialog.open = function....
第一种情况-通过调用newdialog()
创建的每个对象都将具有此打开功能
第二种情况与对话框对象无关,将其视为静态函数。
编辑
在这里找到了一个很好的答案:我认为handleLocationChange是从事件触发对象调用的,该对象注册侦听器,但不注册此
上下文,因此当它被触发时,您不能使用此
,因为它指的是handleLocationChange。为了克服这一点,他们选择设置对此(=self变量)的引用,并使用self调用其他实例函数。基本上,它存储的是一个在创建时已知但在执行handleLocationChange时未知的值
下面是一些显示问题的代码:
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
上面使用的模式不是事件系统(假设它是pulisher订阅服务器),因为事件通常在对象(按钮、输入、对话框)上触发或调用,但如果是更像事件系统的实现,则很容易获得正确的this
上下文,因为您在实例上或从实例触发事件(如myButton或myDialog)
有关类似事件系统的实现,请参见以下代码:
var eventSystem={
add:function(eventname,fnCallback){
if(!this.events[eventname]){
this.events[eventname]=[];
}
this.events[eventname].push(fnCallback);
},
//change in trigger as it's 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
var事件系统={
添加:函数(eventname,fnCallback){
如果(!this.events[eventname]){
this.events[eventname]=[];
}
this.events[eventname].push(fnCallback);
},
//触发器在传递事件对象时发生更改
触发器:函数(事件){
如果(!this.events[event.type]){
返回;
}
var i=0;
对于(i=0;i+1)您是对的,我甚至想补充一点,向原型添加函数将导致该原型的所有现有(和未来)对象“继承”那是一个非常重要的时刻。那么,我可以说,如果我将handleLocationChange声明为原型函数,它不会只关闭一个特定的对话框吗?我知道,handleLocationChange是一个响应特定实例事件的处理程序。你可以在原型上声明它,但你必须触发它呃实例上的事件:instanceOfDialog.trigger(“locationChange”);我猜Angular的事件系统不是这样工作的?@user2422960它可能会出错,因为此
不引用对话框实例,并且self
只能在对话框主体中使用(原型函数在对话框函数体之外)。我已经更新了我的答案,解释了为什么我认为它们在构造函数的主体中包含事件处理程序。handleLocationChange声明为this.handleLocationChange,因此使用新对话框创建的每个对话框不仅具有handleLocationChange,还具有自己的handleLocationChange,而不是与原型上的其他实例共享。I我不知道他们为什么会这样做,但我还没有用Angular,但我很好奇地想知道。我认为它应该被你覆盖。因为它现在所做的一切——调用close()我认为重要的是它调用了self
。关闭。从代码显示来看,两者都是可互换的。你可能太盯着挡住森林视线的几棵树了。编码员在周一写的代码与周五写的代码不同,也许就像我妈妈过去常说的“这就是上帝创造的方式”@dandavis我不这么认为,该函数被放在构造函数体中,这样它就可以利用名为self
的闭包值。这可能是因为handleLocationChange在某个点作为回调变量传递,所以这个
上下文会丢失。有关详细信息,请参阅我的答案。我很好奇为什么会出现这种情况将增加闭包变量和实例拥有的函数的权重,因为还有其他解决方案不需要消耗额外内存。这听起来可能很琐碎,但在像google docs这样的应用程序中,这可以加起来。@HMR:self与此相同,可以从与内联方法相同的原型方法访问。它可以是wired为this.close()或self.handleLocationChange,如果需要,bind或call会在以后处理任何不合适的值。@dandavis您不能在任何原型函数中使用self,因为self在构造函数函数体中声明为var self=this
,因此self不在函数体之外的任何位置(原型函数在函数体外部声明)。这个问题不是另一个问题的重复,因为self变量没有用作private,我怀疑Angular需要它,因为他们的event/Publish/Subscribe系统handleLocationChange是在不引用特定对象的情况下传递的。@HMR。我的意思是,在显示的代码中,self==This。因此,在原型方法中,This.x是the与构造函数中的self.x相同。
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
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
var eventSystem={
add:function(eventname,fnCallback){
if(!this.events[eventname]){
this.events[eventname]=[];
}
this.events[eventname].push(fnCallback);
},
//change in trigger as it's 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