0

The code below is adapted from this answer

    function MessageClass() {
        var self = this;
        this.clickHander = function(e) { self.someoneClickedMe = true; };

        var _private = 0;
        this.getPrivate = function() { return _private; };
        this.setPrivate = function(val) { _private = val; };
    }

    ErrorMessageClass.prototype = new MessageClass();
    function ErrorMessageClass() {
        MessageClass.apply(this, arguments);
    }

    var errorA = new ErrorMessageClass();
    var errorB = new ErrorMessageClass();
    errorA.setPrivate('A');
    errorB.setPrivate('B');
    console.log(errorA.getPrivate()); 
    console.log(errorB.getPrivate());

The original post did not have the MessageClass.apply(this, arguments); since the purpose was to show how inheritance can go wrong in Javascript.

My question is, is saying: ErrorMessageClass.prototype = new MessageClass(); before the ErrorMessageClass constructor has even been declared bad practice? My understanding is that calling undeclared identifiers like that causes a silent declaration to occur, with the result being placed on the global window object, which I understand is bad.

Is this form:

    function ErrorMessageClass() {
        MessageClass.apply(this, arguments);
    }
    ErrorMessageClass.prototype = new MessageClass();

considered to be better practice? This link shows the code written as it was originally above, which is why I even tried it. Does this blogger know something I don't (quite likely)?

EDIT

Lots of great info in the answers below, but I did want to highlight this link which really explains things perfectly

Community
  • 1
  • 1
Adam Rackis
  • 82,527
  • 56
  • 270
  • 393

2 Answers2

3

Usually, to avoid this confusion, you would just attach the prototype after, but as Adam Rackis pointed out, function declarations are hoisted, like var statements.

However, you should not instantiate the base object as the prototype. If your base object takes arguments, what are you supposed to use? Use an empty "surrogate" constructor

// Used to setup inheritance
function surrogate () {};


function MessageClass() {
    var self = this;
    this.clickHander = function(e) { self.someoneClickedMe = true; };

    var _private = 0;
    this.getPrivate = function() { return _private; };
    this.setPrivate = function(val) { _private = val; };
}

// The key steps to creating clean inheritance
surrogate.prototype = MessageClass;
// Sets up inheritance without instantiating a base class object
ErrorMessageClass.prototype = new surrogate();
// Fix the constructor property
ErrorMessageClass.prototype.constructor = ErrorMessageClass

function ErrorMessageClass() {
    MessageClass.apply(this, arguments);
}

There's much more to be said. http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • +1 absolutely, never construct and initialise a real object for use as a prototype. See [this question](http://stackoverflow.com/questions/1595611/how-to-properly-create-a-custom-object-in-javascript) for background and some examples of wrapping up that ‘surrogate’ in a method, supplying a separate initialiser, etc. – bobince Aug 18 '11 at 16:41
  • I mostly agree but you seriously consider using [Object.create()](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create) where it is available and [emulating it](http://javascript.crockford.com/prototypal.html) where it isn't. Not that it changes something, the emulation basically uses the same approach as your code. – Wladimir Palant Aug 18 '11 at 17:05
  • So why would I use that? Because Douglas Crockford says the `new` operator is dangerous? – Ruan Mendes Aug 18 '11 at 17:13
2

It works because function declarations are evaluated first. If you tried to move these classes under an object literal "namespace" the first version would fail.

I personally find the second method to be much easier to read - also, don't forget to set the sub-class' prototype.constructor property back to itself. Personally, I use an inherits() method on the Function prototype which wraps up essentially the type of code you're using here.

Stephen
  • 18,597
  • 4
  • 32
  • 33
  • What do you mean by "function declarations are evaluated first"? Are you saying function ErrorMessageClass() ... will be evaluated before ErrorMessageClass.Prototype = ... even it's declared second? I wouldn't think an interpreted language would be able to do that – Adam Rackis Aug 18 '11 at 16:28
  • This might help - http://stackoverflow.com/questions/3887408/javascript-function-declaration-and-evaluation-order – Stephen Aug 18 '11 at 16:34
  • @Adam: yes. JavaScript is full of surprises! If you don't want ‘function hoisting’, you can write your function as an FunctionExpression rather than a magic FunctionDeclaration statement: `var ErrorMessageClass= function() { ... };` – bobince Aug 18 '11 at 16:44
  • That link explained things beautifully. Thank you. – Adam Rackis Aug 18 '11 at 16:55