24

I'm looking for a simple way of creating two classes, one inheriting from the other, and the child redefining one of the parent's methods, and inside the new method, calling the parent's.

For example, having a class Animal and Dog, where the Animal class defines a method makeSound() which establishes how to output a sound, which Dog then overrides in its own makeSound() method to make a "woof" sound, but while also calling Animal's makeSound() to output that woof.

I looked at John Resig's model here, but it uses the native arguments.callee property which is apparently depreciated in ECMA script 5. Does that mean I shouldn't use John Resig's code?

What would one neat, simple way of writing my animal/dog code using Javascript's prototype inheritance model?

2
  • 1
    A broader scope response: Check out these great video lectures from JSON inventor and Javascript god (Yeah I know all gods also have faults but god he is nethertheless, think Greek gods not "The One") Douglas Crockford: playlists youtube.com/playlist?list=PL5586336C26BDB324 and youtube.com/playlist?list=PL7664379246A246CB Commented Feb 24, 2013 at 10:31
  • The following answer contains code for creating instances of a certain type, inheriting from them and overriding/extending parent functions, maybe it'll be helpful: stackoverflow.com/a/16063711/1641941 Commented Dec 7, 2013 at 8:56

4 Answers 4

27

Does that mean I shouldn't use John Resig's code?

Correct, not when you are using ES5 in strict mode. However, it can be easily adapted:

/* Simple JavaScript Inheritance for ES 5.1
 * based on http://ejohn.org/blog/simple-javascript-inheritance/
 *  (inspired by base2 and Prototype)
 * MIT Licensed.
 */
(function(global) {
  "use strict";
  var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  function BaseClass(){}

  // Create a new Class that inherits from this class
  BaseClass.extend = function(props) {
    var _super = this.prototype;

    // Set up the prototype to inherit from the base class
    // (but without running the init constructor)
    var proto = Object.create(_super);

    // Copy the properties over onto the new prototype
    for (var name in props) {
      // Check if we're overwriting an existing function
      proto[name] = typeof props[name] === "function" && 
        typeof _super[name] == "function" && fnTest.test(props[name])
        ? (function(name, fn){
            return function() {
              var tmp = this._super;

              // Add a new ._super() method that is the same method
              // but on the super-class
              this._super = _super[name];

              // The method only need to be bound temporarily, so we
              // remove it when we're done executing
              var ret = fn.apply(this, arguments);        
              this._super = tmp;

              return ret;
            };
          })(name, props[name])
        : props[name];
    }

    // The new constructor
    var newClass = typeof proto.init === "function"
      ? proto.hasOwnProperty("init")
        ? proto.init // All construction is actually done in the init method
        : function SubClass(){ _super.init.apply(this, arguments); }
      : function EmptyClass(){};

    // Populate our constructed prototype object
    newClass.prototype = proto;

    // Enforce the constructor to be what we expect
    proto.constructor = newClass;

    // And make this class extendable
    newClass.extend = BaseClass.extend;

    return newClass;
  };

  // export
  global.Class = BaseClass;
})(this);
Sign up to request clarification or add additional context in comments.

6 Comments

This works realy well, and to make sure it still works in legacy IE, you could include this polyfill if (!Object.create) { Object.create = (function(){ function F(){} return function(o){ if (arguments.length != 1) { throw new Error('Object.create implementation only accepts one parameter.'); } F.prototype = o; return new F() } })(); }
This causes instances generated using Class.extend() to have a displayed type of Class.extend.init and subclasses to have a displayed type of proto.(anonymous function). Resig's original snippet gives all instances a displayed type of Class. That said, running * instanceof Class returns true for this rewrite, but it's still distracting and ugly in the browser console.
@Adrian: If you don't like your debugger's types, use named functions (for the init property). I've also named the default constructors now.
Aha. Silly me. I should not try to read JS code while tired. Thanks for the update. :)
What i understand is that init is like your constructor is there a destructor method which gets called before the object is getting destroyed?
|
7

Prototype chain with Object.create() + assign constructor

function Shape () {
    this.x = 0;
    this.y = 0;
}

Shape.prototype.move = function (x, y) {
    this.x += x;
    this.y += y;
};

function Rectangle () {
    Shape.apply(this, arguments); // super constructor w/ Rectangle configs if any
}

Rectangle.prototype = Object.create(Shape.prototype); // inherit Shape functionality
// works like Rectangle.prototype = new Shape() but WITHOUT invoking the constructor

Rectangle.prototype.constructor = Rectangle;

var rect = new Rectangle();

rect instanceof Rectangle && rect instanceof Shape // returns true

from Object.create documentation

information about the new keyword

1 Comment

This is the way to do it in 2015
3

This is something I came up with for inheritance using chaining as well as allowing _super to work.

/**
 * JavaScript simple inheritance
 * by Alejandro Gonzalez Sole (base on John Resig's simple inheritance script)
 * MIT Licensed.
 **/
(function (){
    var initializing = false,
      fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.* /;

    function Class(){};

    function inheritClass(superClass){
      var self = this;
      function Class(){
        if (!initializing && typeof this._constructor === 'function')
          this._constructor.apply(this, arguments);
      }

      Class.prototype = superClass.prototype;
      Class.prototype._constructor = superClass;
      Class.prototype.constructor = Class;
      Class.extend = extendClass;
      //currenlty if you inhert multiple classes it breaks
      Class.inherit = inheritClass;
      return Class;
    };

    function extendClass(prop) {
      var self = this;
      var _super = self.prototype;

      function Class(){
        if (!initializing && typeof this._constructor === 'function')
          this._constructor.apply(this, arguments);
      }

      initializing = true;
      var prototype = new self();
      initializing = false;

      for (var name in prop) {
        prototype[name] = typeof prop[name] == "function" &&
          typeof _super[name] == "function" && fnTest.test(prop[name]) ?
          (function(name, fn){
            return function() {
              var tmp = this._super;
              this._super = _super[name];
              var ret = fn.apply(this, arguments);
              this._super = tmp;
              return ret;
            };
          })(name, prop[name]) : prop[name];
      }

      Class.prototype = prototype;
      Class.prototype.constructor = Class;
      Class.extend = extendClass;
      Class.inherit = inheritClass;

      return Class;
    };

    Class.extend = extendClass;
    Class.inherit = inheritClass;

})();


//EXAMPLE

function Person(){
  this.name = "No name";
  console.log("PERSON CLASS CONSTRUCTOR")
}
Person.prototype.myMethod = function (t){
  console.log("MY PERSON", t, this.name);
  return -1;
}

var TestPerson = Class.inherit(Person).extend({
    _constructor: function(){
      this._super();
      this.name = "JOhn";
      console.log("TEST PERSON CONSTRUCTOR");
    },
    myMethod: function (t){
      console.log("TEST PERSON", t, this.name);
      return this._super(t)
    }
});


var test = new TestPerson();

console.log(test.myMethod("BA"));

I've been testing it on my pixi wrapper https://github.com/guatedude2/pixijs-cli so far it's been working very well for me.

The only issue i've encountered with this approach is that you can only inherit once. If you run inherit again it will override the previous inheritance.

1 Comment

I did write an example and is just this is just a different approach. I updated the latest version i'm using. It's similar to the one that Resig wrote but it adds inheritance to prototype classes and works on ES6.
2

I prefer the way TypeScript generates a form of inheritance (Select Simple Inheritance from the dropdown). That one doesn't use arguments.callee, but an __extends prototype.

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var Animal = (function () {
    function Animal(name) {
        this.name = name;
    }
    Animal.prototype.move = function (meters) {
        alert(this.name + " moved " + meters + "m.");
    };
    return Animal;
})();
var Snake = (function (_super) {
    __extends(Snake, _super);
    function Snake(name) {
        _super.call(this, name);
    }
    Snake.prototype.move = function () {
        alert("Slithering...");
        _super.prototype.move.call(this, 5);
    };
    return Snake;
})(Animal);
var Horse = (function (_super) {
    __extends(Horse, _super);
    function Horse(name) {
        _super.call(this, name);
    }
    Horse.prototype.move = function () {
        alert("Galloping...");
        _super.prototype.move.call(this, 45);
    };
    return Horse;
})(Animal);
var sam = new Snake("Sammy the Python");
var tom = new Horse("Tommy the Palomino");
sam.move();
tom.move(34);

Comments

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.