了解更好的Javascript OOP架构

时间:2013-06-26 07:41:17

标签: javascript oop architecture prototype

当我阅读Angularjs的UI附加组件的一些示例时,我偶然发现了一些代码,这些代码向我展示了我对Javascript的了解非常可行:

以下是Angular提供程序中的一个类:

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()

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

            // .. some code
        };

现在我不明白为什么该函数被声明为原型,但是在类本身内部是handleLocationChange

我如何决定选择哪种方法?

可以找到完整的要点here

3 个答案:

答案 0 :(得分:3)

考虑以下两种情况:

Dialog.prototype.open = function...

Dialog.open = function....

第一种情况 - 通过调用new Dialog()创建的每个对象都将具有此open函数

第二种情况与对象对象无关,将其视为静态函数。

修改

在这里找到了一个很好的答案:javascript-class-method-vs-class-prototype-method

答案 1 :(得分:2)

使用新的Dialog()创建

函数open将由所有对象共享。并且handleLocationChange对于不同的对象将是不同的。

答案 2 :(得分:2)

我认为handleLocationChange是从事件触发器对象调用的,它注册了侦听器但没有注册this上下文,因此当它被触发时,你不能使用this,因为它引用了handleLocationChange。为了克服这一点,他们选择设置closure对此的引用(=自变量)并使用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

以下是如何解决设置闭包引用

的问题
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

以下是对事件系统的修复,以处理this上下文:

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

上面使用的模式不是一个事件系统(认为它是pulisher订户),因为事件通常是从一个对象(按钮,输入,对话框)触发或调用的,但是如果有更多的事件系统,就像实现它会是因为您在实例上触发事件(例如myButton或myDialog),所以很容易获得正确的this上下文。

请参阅以下代码了解实现事件系统:

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

不确定为什么Angular会选择通过使用闭包“打破”原型,因为示例显示还有其他选择。也许有人可以解释谁更熟悉Angular。