3

I want to create a Javascript class/object that allow me to have various method:

Model class

  • Model.all() » static method
  • Model.find() » static method
  • Model delete() » instance method
  • Model save() » instance method
  • Model.create() » static that returns a new Model instance

For static method I can define them using:

Model.staticMethod(){ method }

while for instance method is better to use:

function Model(){
    this.instanceMethod = function(){}    
}

and then create a new instance

or using prototype?

var m = function Model(){

}

m.prototype.method() = function() {
}

Now let's say that I want to create a new class based on Model, how to inherit not only its prototypes but also its static methods?

EDIT:

to avoid confusion this is more or less what I want to create:

http://activejs.org/activerecord/index.html and http://activejs.org/activerecord/ActiveRecord/Model/index.html

where I can define a new model using something like that

var User = ActiveRecord.create({
    username: '',
    password: '',
    post_count: 0,
    profile: ''
}

then create an instance

var jessica = User.create({
    username: "Jessica",
    password: "rabbit"
});

use instance methods like

jessica.save();

but also class methods:

User.findByUsername('Jessica');
9
  • Yes you should use prototype, otherwise you create new function objects for every object you instantiate. Commented Jun 19, 2012 at 9:05
  • possible duplicate of Use of 'prototype' vs. 'this' in Javascript? Commented Jun 19, 2012 at 9:11
  • Regarding your last question, you probably have to iterate over the methods you added to Model and assign them to the new class as well. Commented Jun 19, 2012 at 9:14
  • so not something referred to the whole class? because to me seems a bit strange to do var a = new Model() and then a.all since a represents a single istance it shouldn't be used to get all the models from the server (all() should do this) Commented Jun 19, 2012 at 9:16
  • 2
    There are no real static methods. Everything is an object and everything can have properties. If you assign Model.all = ..., then later you call Model.all(). var a = new Model(); a.all() would not even work. Or maybe you misunderstood my comment and I yours. Commented Jun 19, 2012 at 9:27

4 Answers 4

5
function Model() {}

// Methods in the instantiated object
Model.prototype = {
    constructor: Model,

    // Note that "delete" is a reserved word, so we need quotes
    'delete': function() {},

    save: function() {}
};

// Static methods
Model.all = function() {};

Model.find = function() {};

Model.create = function() {
    return new Model();

    // To be more generic, you can also:
    return new this();
};

When you use var InheritedModel = Object.create( Model );, it also inherits the static methods.

var InheritedModel = Object.create( Model );
!!InheritedModel.all // true

However, you can't do new InheritedModel(), because it's not a function, and using Object.create( InheritedModel ) won't give you the instance methods.

If you want to instantiate the inherited class using new, you need the following:

function InheritedModel() {}

InheritedModel.prototype = Object.create( Model.prototype );

// Copy all the static methods in the InheritedModel object
Object.keys( Model ).forEach( function( key ) {
    InheritedModel[ key ] = Model[ key ];
} );

Edit: after seeing your edit, here is the solution I'd recommend you:

function ActiveRecord( type, args ) {
    if ( type = 'users' ) {
        return new this.users();
    }
}

// Static method on ActiveRecord
ActiveRecord.create = function( type, args ) {
    return new ActiveRecord( type, args );
};

ActiveRecord.prototype = {
    constructor: ActiveRecord,

    // Instance method on ActiveRecord, you won't need it,
    // but your constructor does
    users: function( args ) {}
};

var Users = ActiveRecord.prototype.users;

Users.prototype = {
    constructor: Users,

    // Instance method on User's instance
    save: function() {}
}

// Static method on User
Users.create = function() {}
Sign up to request clarification or add additional context in comments.

9 Comments

how to inherit static methods with this?
@MatteoPagliazzi by using Object.create
@MatteoPagliazzi Edited to add another solution
this seems exactly what i need! only one question: this way each inherited model will create a new function for model.all it won't use the same like when using prototype right? this may cause big problems with performances or it's negligible?
@MatteoPagliazzi be careful, I edited the "create InheritedModel" stuff. And it will not create a prototype, it still uses Model methods in the prototype, not creating a new one.
|
4

Commented code is pseudo code. You can achieve the same by:

var modelInstanceMethods = {

    save: function() {
        /*
        insert into this.tableName blabla
        */
    },

    'delete': function() {
        /*
        delete from this.tableName blabla
        */
    }
};

var modelStatics = {

    create: function(obj) {
        return new this(obj);
    },

    all: function() {
        /*
        return select * from this.tableName.map( function( values ) {
            return new this(values);
        },this);
        */
    },

    find: function(id) {
        /*
        select * from this.tableName where id = id
        return new this(columnValues);
        */
    }


};

var ActiveRecord = {
    create: function( tableName, fields, methods ) {

        function Model( obj ) {
            this.tableName = tableName;
            this.fields = {};
            if( fields ) {
                for( var field in fields ) {
                    this.fields[field] = fields[field];
                }
            }

            if( obj ) {
                for( var field in obj ) {
                    this.fields[field] = obj[field];
                }       
            }
        }

        Model.tableName = tableName;
        Model.prototype = Object.create(modelInstanceMethods);
        Model.prototype.constructor = Model;

        for( var key in modelStatics ) {
            Model[key] = modelStatics[key];
        }

        if( methods ) {
            for( var key in methods ) {
                Model.prototype[key] = methods[key];
            }   
        }

        return Model;
    }
};

Usage

var User = ActiveRecord.create('users',
    /* fields and their default values */
{
    id: 0,
    username: '',
    password: '',
    post_count: 0,
    profile: ''
}, {
    /*instance methods */
    setPassword: function(password) {
        this.fields.password = password;
    }
});

/*You can define static methods like this */

User.findByUsername = function(username) {
    /*select from this.tableName where userName = username
               return new this(columnValues) blabla
             */
};

var jessica = User.create( {
    username: "Jessica",
    password: "rabbit"
});

jessica.save();

User.findByUsername('Jessica');

1 Comment

I really like this implementation, I'l study it later. thanks so much!
1

You can extend the Object's prototype adding an "extends" method to Object.prototype. This way you will have an inherithance method that look like java.

(it's important to define the "extends" property as not enumerable otherwise it will break jQuery)

Object.defineProperty(Object.prototype, "extends", {
        "enumerable": false,
        "value" : function(constructor) {

                /*Inheriting enumerable statick method and paramether
                from the super class */
                Object.keys( constructor ).forEach( function(key) {
                        this[key]= constructor[key];
                }.bind(this));

                /*Classic Javascript inheritance*/
                this.prototype= Object.create( constructor.prototype, {
                            "constructor": {
                                    "value": this,
                                    "configurable": true
                            }
                });             

                this.__super__= constructor;

        }

});

After you can easily inherit one class by an other by doing so:

InheritedModel.extends(Model);

function InheritedModel(params){

       this.constructor.__super__.call(this, params);

       /* Or directly :
       Model.call(this,param);
       */

       /*Code specific to InheritedModel constructor */
}

/*To overload method from the super class:*/

InheritedModel.prototype.foo= function(params){

     var out= this.constructor.__super__.prototype.foo.call(this,params);

     /* Or
     var out= Model.prototype.foo.call(this,param);
     */

     /* code */

};

InheritedModel will inherit of all the instance method and all static method from model.

Example:

function Model() {
        this.inheritedClassName= "Model";
};

Model.inheritedClassName= "Model";

Model.getClassName = function() {
       return this.name;
};

Model.prototype.getClassName = function() {
        return this.constructor.name;
};

InheritedModel.extends(Model);

function InheritedModel() {
        Model.call(this);
}

console.log(InheritedModel.inheritedClassName);/* Model */
console.log(InheritedModel.getClassName());/* InheritedModel */

var inheritedModel= new InheritedModel();

console.log(inheritedModel.inheritedClassName);/* Model */
console.log(inheritedModel.getClassName());/* InheritedModel */

I think this is the best solution.

Comments

-1

what about this, you have private and public methods:

function Model() {
    var privateMethods = {
       private1: function() {},
       private2: function() {},
       private3: function() {},
    };

    var publicMethods = {
    method1: function() {},
    method2: function() {
            //call a private method...
            privateMethods.private1();
        }
    };

    return publicMethods;
}


// Static methods
Model.all = function() {};

Model.find = function() {};

Model.create = function() {
    return new Model();
};

1 Comment

You cannot use inheritance with this

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.