Question about a particular pattern of Javascript class definition
Recently I saw the following code that creates a class in javascript:
var Model.Foo = function(){
// private stuff
var a, b;
// public properties
this.attr1 = '';
this.attr2 = '';
if(typeof Model.Foo._init === 'undefined'){
Model.Foo.prototype = {
func1 : functio开发者_开发百科n(){ //...},
func2 : function(){ //... },
//other prototype functions
}
}
Model.Foo._init = true;
}
// Instantiate and use the class as follows:
var foo = new Model.Foo(); foo.func1();
I guess the _init variable is used to make sure we don't define the prototypes again. Also, I feel the code is more readable since I am placing everything in a function block (so in oop-speak, all attributes and methods are in one place). Do you see any issues with the code above? Any pitfalls of using this pattern if I need to create lots of classes in a big project?
This is a weird Javascript pattern that I would never use to develop object-oriented JS code. For one thing, Model.Foo._init === 'undefined'
never evaluates to true
if Model.Foo._init
is anything but the string 'undefined'
; therefore, the code
Model.Foo.prototype = {
func1 : function(){ /* ... */},
func2 : function(){ /* ... */},
//other prototype functions
}
will not run unless that condition holds true. (Perhaps the author meant to add a typeof
, as in typeof Model.Foo._init === 'undefined'
? I don't know.)
Addressing your concern about "[making] sure we don't define the prototypes again", this is already achieved with:
Model.Foo = function() {
// private stuff
var a, b;
// public properties
this.attr1 = '';
this.attr2 = '';
};
Model.Foo.prototype = {
func1 : function() { /* ... */},
func2 : function() { /* ... */}
//,other prototype functions
};
// Instantiate and use the class as follows:
var foo = new Model.Foo();
foo.func1();
which is along the lines of what I recommend if you aren't using a framework.
Basically, the answer to your question is: if you use this non-standard pattern for development, then other programmers, maybe even yourself a few months later, will find it difficult to extend and work with.
It just seems unnecessarily complex. You need to be disciplined to not use any parameters or local variables of Model.Foo in the implementation of the prototype extension. Its odd to overwrite the entire .prototype object and not just add individual members as well. Why not just do this the normal way?
var Model.Foo = function(){
// private stuff
var a, b;
// public properties
this.attr1 = '';
this.attr2 = '';
}
Model.Foo.prototype.func1 = function(){ //...};
Model.Foo.prototype.func2 = function(){ //... };
alternate allowing per-instance member variables private
var Model.Foo = function(){
// private stuff
var a, b;
// public properties
this.attr1 = '';
this.attr2 = '';
this.func1 = function(){ //...};
this.func2 = function(){ //... };
}
A few things stand out as potentially troublesome:
Foo.prototype
is set to a simple object, which can't extend anything using this pattern.- Where is the "private stuff" used? Every time you create a new object, you create new private variables, which seemingly can only be used in the functions defined in
Foo.prototype
, which should only be run once.
It's kind of a mess, and there are details/examples all over the web of better was to do this.
The following example illustrates a pattern that I personally developed over time.
It exploits scoping to allow private fields and methods.
Employee = (function(){
// private static field
var staticVar;
// class function a.k.a. constructor
function cls()
{
// private instance field
var name = "";
var self = this;
// public instance field
this.age = 10;
// private instance method
function increment()
{
// must use self instead of this
self.age ++;
}
// public instance method
this.getName = function(){
return cls.capitalize(name);
};
this.setName = function(name2){
name = name2;
};
this.increment = function(){
increment();
};
this.getAge = function(){
return this.age;
};
}
// public static field
cls.staticVar = 0;
// public static method
cls.capitalize = function(name){
return name.substring(0, 1).toUpperCase() +
name.substring(1).toLowerCase();
};
// private static method
function createWithName(name)
{
var obj = new cls();
obj.setName(cls.capitalize(name));
return obj;
}
return cls;
})();
john = new Employee();
john.setName("john");
mary = new Employee();
mary.setName("mary");
mary.increment();
alert("John's name: " + john.getName() + ", age==10: "+john.getAge());
alert("Mary's name: " + mary.getName() + ", age==11: "+mary.getAge());
精彩评论