Extending prototypes in Javascript - good way?

20,185

Solution 1

In general your approach will work but a better approach will be to replace:

Child.prototype = new Parent;

with:

Child.prototype = Object.create(Parent.prototype);

This way you don't need to call new Parent, which is somewhat an anti-pattern. You could also define new properties directly as follows:

Child.prototype = Object.create(Parent.prototype, {
  setPosition: {
    value: function() {
      //... etc
    },
    writable: true,
    enumerable: true,
    configurable: true
  }
});

Hope this helps.

Object.create() at MDN

Solution 2

Your approach it is a good pure JavaScript approach. The only get away from tipping "Child.prototype" every time is to put it in a reference variable.

Like:

  var children = Child.prototype;
  children.init = function(){ /*/someoverridecode*/}

But you are still doing the Child.prototype behind this. You can also define a function that does this for you, see bind of underscore, maybe it suits your needs.

Cheers

Solution 3

I may get deep fried for this suggestion as there are several articles that can argue against some practices in my example, but this has worked for me and works well for clean looking code, stays consistent, minifies well, operates in strict mode, and stays compatible with IE8.

I also like utilizing the prototype methodology (rather than all of the 'extend' or 'apply' styles you see everywhere).

I write out my classes like this. Yes, it looks a lot like an OOP language which you didn't want, but it still adheres to the prototypical model while holding similarities to other familiar languages which makes projects easier to navigate.

This is my style I prefer :) I'm not saying it's the best, but it's so easy to read.

(function(ns) {

  var Class = ns.ClassName = function() {

  };

  Class.prototype = new baseClass();
  Class.constructor = Class;
  var _public = Class.prototype;
  var _private = _public._ = {};

  Class.aClassProperty = "aValue";

  Class.aClassMethod = function(params) {

  }

  _public.aMethod = function(params) {
      _private.myMethod.call(this, "aParam");
      Class.aClassMethod("aParam");
  }

  _private.myMethod = function(params) {

  }

})({});

EDIT:

I went ahead and converted your example this style just to show you what it would look like:

var namespace = {};

(function(ns) {

    var Class = ns.Parent = function() {

    };

    var _public = Class.prototype;
    var _private = _public._ = {};

    _public.init = function() {
        this.name = "anon";
    }

    _public.initWithParameters = function(parameters) {
        this.name = parameters.name ? parameters.name : "anon";
    }

    _public.talk = function() {
        console.log('Parent is: ' + this.name);
    }

})(namespace);

(function(ns) {

    var Class = ns.Child = function() {
        this.position = {x:0, y:0};
    };

    Class.prototype = new ns.Parent();
    Class.constructor = Class;
    var _public = Class.prototype;
    var _private = _public._ = {};

    _public.init = function() {
        _public.init.call(this);
        this.setPosition(0, 0);
    }

    _public.initWithParameters = function(parameters) {
        _public.initWithParameters.call(this, parameters);
        this.setPosition(parameters.pos.x, parameters.pos.y);
    }

    _public.setPosition = function(x, y) {
        this.position.x = x;
        this.position.y = y;
    }

    _public.talk = function() {
        console.log('Child is: ' + this.name + ' and location is: ' + this.position.x + ', ' + this.position.y);
    }

})(namespace);

Solution 4

Coming from google in 2019.

From the latest MDN documentation, the way to extend prototype is :

function MyClass() {
  SuperClass.call(this);
}

// inherit one class
MyClass.prototype = Object.create(SuperClass.prototype);
// mixin another
Object.assign(MyClass.prototype, {
  //... you own prototype ...
});

// re-assign constructor
MyClass.prototype.constructor = MyClass;
Share:
20,185
Jem
Author by

Jem

Updated on November 13, 2020

Comments

  • Jem
    Jem over 3 years

    I want to validate that the approach I'm using is correct when it comes to extend a prototype - supposing "extend" is the right word.

    This topic gets a lot of clones. I'm still trying to properly understand this topic...

    The purpose is: - to write clean and good code. - to avoid using frameworks, if possible plain Javascript. - get advice on the clean frameworks that don't twist JS to obtain class-enabled behaviors.

    Here is the Parent prototype of my sandbox:

    function Parent(){
    
    }
    
    Parent.prototype = {
    
        "init":function(){
    
            this.name = "anon";
        },
    
        "initWithParameters":function(parameters){
    
            this.name = parameters.name ? parameters.name : "anon";
        },
    
        "talk": function(){
    
            console.log('Parent is: ' + this.name);
        }
    }
    

    Now the Child prototype - it adds a "position" property and redefines the behaviors:

    function Child(){
    
        Parent.call(this);
    }
    
    
    Child.prototype = new Parent;
    Child.prototype.constructor = Child;
    
    Child.prototype.init = function(){
    
        Parent.prototype.call(this);
    
        this.setPosition(0, 0);
    }
    
    Child.prototype.initWithParameters = function(parameters){
    
        Parent.prototype.initWithParameters.call(this, parameters);
    
        if(!this.position){
    
            this.position = {x:0, y:0};
        }
    
        this.setPosition(parameters.pos.x, parameters.pos.y);
    }
    
    Child.prototype.setPosition = function(x, y){
    
        this.position.x = x;
        this.position.y = y;
    }
    
    Child.prototype.talk = function(){
    
        console.log('Child is: ' + this.name + ' and location is: ' + this.position.x + ', ' + this.position.y);
    }
    

    Is this a good practice? Is there no shorthand to avoid writing "Child.prototype." when overriding a property (using a litteral maybe, like the Parent prototype is written).

    I know of J. Resig's Class/extend approach. But I'd rather use Javascript as the prototypical language it is, not make it work as a "class-like behaving class-less OO language".

    Thanks for your help :-)