开发者

Question about a particular pattern of Javascript class definition

开发者 https://www.devze.com 2023-01-01 05:17 出处:网络
Recently I saw the following code that creates a class in javascript: var Model.Foo = function(){ // private stuff

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:

  1. Foo.prototype is set to a simple object, which can't extend anything using this pattern.
  2. 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());
0

精彩评论

暂无评论...
验证码 换一张
取 消