3

I am new to JavaScript (started learning it this week). I've completed the CodeCademy course (actually just Objects 1 && 2 parts, rest was boring). I thought I learned prototypal inheritance with constructors, but than I've started watching Douglas Crockford: Advanced JavaScript

Right at the beginning of the video, he mentions 2 types of inheritance, and I've realized, everything I thought I knew, I don't know. So I've started playing with Chrome JavaScript console and tried to do something using both inheritance models.

//create constructor
function MyParent(){ 
    this.name = "joe";
    this.sayHi = function(){ 
        console.log(this.name); 
    }
}

//create child object and inherit from parent
var myChild1 = new MyParent();
myChild1.name
"joe"
myChild1.sayHi()
joe

//add method to MyParent, so it can be accessed from myChild1
MyParent.prototype.sayBye = function(){ console.log("bye");}

myChild1.sayBye()
bye

//create another constructor
function MyParent2(){
    this.sayHi = function(){ 
        console.log("hi"); 
    }
}

//make it inherit from MyParent, overwriting sayHi if object is instance of MyParent2
MyParent2.prototype = new MyParent();

//overwrote sayHi
var myChild11 = new MyParent2(); 
myChild11.sayHi();
hi

//still same as MyParent as its not overwriten in MyParent2
myChild11.name
"joe"

Than I've tried to do the same thing using Object.create:

//create an object
 var myParent = { 
    name : "joe",
    sayHi : function(){ 
        console.log(this.name) 
    } 
};

//it works
myParent.sayHi()
joe

//create child object
var myChild = Object.create(myParent);
myChild.sayHi()
joe

//add bye method to myChild
myChild.sayBye = function(){ console.log("bye"); }
myChild.sayBye();
bye

//add bye method to myParent - does not overwrite child's bye
myParent.sayBye = function(){ console.log("bye2"); }

//however adding sayBye2 to parent, becomes available on already created child
myParent.sayBye2 = function(){ console.log("bye2"); }
myChild.sayBye2();
bye2
//but its the parent property
myChild.hasOwnProperty("sayBye2")
false

//could make million objects inherit from each other vertically or horizontally
var myChild2 = Object.create(myChild);
myChild2.name
"joe"

So far, just by first impression, I do like both models, maybe I slightly prefer the latter. It seems to be more expressive and powerful.

I've done some search on the vs. topic, but couldn't find a good article, that would explain pros and cons of each approach (other than latter is supported by newer browsers only, but there is a simple workaround). Rest of the posts I found were just tl: dr; and somewhat boring.

So my question is, which type of inheritance should I go with. Are there any perks associated with either? Any major cons for either? Can you mixin both? (eg. I know you can pass object as argument into new Constructor()))

Thanks

EDIT: Looking at trick to create Object.create() alternative if browser doesn't support it, I realized how simple the hack is. It is basically creating an empty constructor, assigning parent object as its prototype and returning the built constructor as new object. What a neat hack!

if(typeof Object.create !== "function") { Object.create = function (o) { function F() {}; F.prototype = o; return new F(); }; }

But what are the implications? Any gotchas to be aware of?

if __name__ is None
  • 11,083
  • 17
  • 55
  • 71

2 Answers2

3

Both ways are equally powerful. Some says that you are not a real JavaScript master if you do not embrace the pure prototypal approach, but really everything is still just prototypal inheritance and it's a matter of style.

Personnaly, I prefer using a classical approach and make use of the new keyword together with a constructor function.

Why?

  • The classical model is very well-know.
  • I don't have to implement an init function on my objects only to do what a constructor does already. As you probably noticed, the pure prototypal approach leaves you without a constructor so you will have to implement an initialization function (generally called init) if you need to run some initialization logic for newly created objects.

However, I find that the pure prototypal approach is a bit less verbose and might be easier to understand for programmers without a strong background with languages implementing classical inheritance.

By the way, one thing you should avoid with the classical model is to inherit using the new keyword like you are doing above because you are running the parent constructor logic unnecessary and it could potentially have undesirable side-effects.

You should be using Object.create like below:

function A(val) { this.val = val; }
A.prototype.alertVal = function () { alert(this.val); };

function B(val) {
    A.call(this, val); //call parent constructor
}

B.prototype = Object.create(A.prototype); //inherit from A
var b = new B('test');
b.alertVal(); //test

At the end, it's all a matter of taste and I respect both ways. Maybe someone else will want to improve this answer with some real advantages/disadvantages, but I cannot find any.

plalx
  • 42,889
  • 6
  • 74
  • 90
  • How does A.apply method work? What does it do in this case exactly? – if __name__ is None Apr 08 '13 at 06:12
  • Is it invoking A constructor, and passing this (scope of B) and arguments (again, of B) to A? – if __name__ is None Apr 08 '13 at 06:20
  • @JanNetherdrake I was writing something, but you got it, it makes sure that the `A` constructor logic gets applied on the newly created instance of `B` by setting the context object (the object to wich `this` points to). If you would have called `A();`, the context object would have been `window` and that's not what you want. Now, for the differences between `apply` and `call`, I suggest you go read about it ;) I updated the example so that it makes a bit more sense to call the parent's constructor. – plalx Apr 08 '13 at 06:25
  • I'm a bit confused with apply/call tho. Is the point of using apply/call the fact, that you are passing 'this' of the outer to the invoked function? I suppose if you had 2 methods, inner and outer (inner being sort of a closure), if you hadn't used 'apply', you would need to put 'this' in a variable, so it could be scoped to the closure, else it would be unacessible, since the closure would have own 'this'? Correct me if I'm misunderstanding. – if __name__ is None Apr 12 '13 at 03:48
  • @JanNetherdrake, To make it simple, unless `new`, `call` or `apply` is used when calling a function, `this` will point to the `object` on wich the function was invoked (the object on the left side of the `.`) in `myObject.someFunction();`. However, if `someFunction();` is invoked this way, it's the same as doing `window.someFunction();`, so `this` will point to `window`. Now, with the above case, we want the `A` function to have it's context object (`this`) set to the new `B` instance. Since calling `A(val);` would result in `this` pointing to `window`, we have to use `A.apply(this, val);`. – plalx Apr 12 '13 at 12:45
  • @JanNetherdrake, A `closure` can be an alternative way of accessing an object declared outside a specific `function`. However, to create a closure over a variable, the function has to be declared in the same (or lower) `scope` as that variable. In the case above, the `A` function is declared outside the function `B`, therefore we cannot use closures in that case. *Please note that using closures will not change the context object `this` in any way.* – plalx Apr 12 '13 at 12:56
  • I now understood the concept of 'this' correctly. Thanks for explaining it to me. By closure I meant something that is not in your example. For example, returning a function from function would be a closure. If I invoke 'this' within such closure, would it be the same 'this' as in main function? eg. function main(){ /* this */ return { ok : function(){ return this; }; } } Are 'this' in main and 'this' in 'ok' same? – if __name__ is None Apr 12 '13 at 20:34
  • @JanNetherdrake, No, closures have nothing to do with `this` and they don't affect the way `this` works in any ways. Have a look at http://stackoverflow.com/questions/111102/how-do-javascript-closures-work – plalx Apr 13 '13 at 03:53
0

Constructing objects with new was pronounced evil, because you can forget to use it and then whatever properties you add to this will end up added to the global object. For this reason I am in for the second approach.

One other thing I have learnt is that you should not try to apply "classical" OOD to JavaScript. It has its own way that should be understood and it is quite nice in fact. I think most of object programming problems in JavaScript come from attempts to stretch notion of "class" onto JavaScript.

Roman Saveljev
  • 2,544
  • 1
  • 21
  • 20
  • Yes, Crockford said the reason why constructor names start with capital letter is to try and remind us to use 'new'. I am very open to different, this is why I'm quite enjoying JS. So I will not try and think like Java in JS. – if __name__ is None Apr 08 '13 at 04:42