1

I'm still trying to get to grips with how this JS inheritance stuff works and each time I think I have it... I clearly don't.

Attempt 1

link: https://jsfiddle.net/jacquesvanderhoven/4qkvdn46/

Code

var baseSettings = function ()
{
    this.message = "base message";
    this.getMessage = function(){
        return  "base message from function";
    }
};

var homeSettings = function ()
{
    this.name = "name";
    this.getName = function()
    {
        return "name from function";
    };
};

homeSettings.prototype = Object.create(baseSettings);

var $log = $("#log");
var base = new baseSettings();
var home = new homeSettings();

$log.html($log.html() + "<br /> baseSettings.message: '" + base.message + "'");
$log.html($log.html() + "<br /> baseSettings.getMessage(): '" + base.getMessage() + "'");
$log.html($log.html() + "<br /> homeSettings.name: '" + home.name + "'");
$log.html($log.html() + "<br /> homeSettings.getName(): '" + home.getName() + "'");
$log.html($log.html() + "<br /> homeSettings.message: '" + home.message + "'");
$log.html($log.html() + "<br /> homeSettings.getMessage(): '" + home.getMessage() + "'");

Results

baseSettings.message: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: ''
homeSettings.getName(): 'name from function'
homeSettings.message: 'undefined'

Two of these actually throw JS exceptions.


Attemp 2

If I add a call to the super constructor it changes things a bit:
Link: https://jsfiddle.net/jacquesvanderhoven/4qkvdn46/5/

Code

var baseSettings = function ()
{
    this.message = "base message";
    this.getMessage = function(){
        return  "base message from function";
    }
};

var homeSettings = function ()
{
    baseSettings.call(this);
    this.name = "name";
    this.getName = function()
    {
        return "name from function";
    };
};

homeSettings.prototype = Object.create(baseSettings);

var $log = $("#log");
var base = new baseSettings();
var home = new homeSettings();

$log.html($log.html() + "<br /> baseSettings.message: '" + base.message + "'");
$log.html($log.html() + "<br /> baseSettings.getMessage(): '" + base.getMessage() + "'");
$log.html($log.html() + "<br /> homeSettings.name: '" + home.name + "'");
$log.html($log.html() + "<br /> homeSettings.getName(): '" + home.getName() + "'");
$log.html($log.html() + "<br /> homeSettings.message: '" + home.message + "'");
$log.html($log.html() + "<br /> homeSettings.getMessage(): '" + home.getMessage() + "'");

Results

baseSettings.message: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: '' (Note! This is empty and shouldn't be)
homeSettings.getName(): 'name from function'
homeSettings.message: 'base message'
homeSettings.getMessage(): 'base message from function'

Most of them work, but I can't figure out why homeSettings.name returns nothing?

It's starting to look to me like Object.create does nothing really, or rather it requires the call to the super-constructor to make any difference, but even then it's not perfect.


Attempt 3

Remove the call to Object.create and leave the call to the super constructor.
Link: https://jsfiddle.net/jacquesvanderhoven/4qkvdn46/8/

Code

var baseSettings = function ()
{
    this.message = "base message";
    this.getMessage = function(){
        return  "base message from function";
    }
};

var homeSettings = function ()
{
    baseSettings.call(this);
    this.name = "name";
    this.getName = function()
    {
        return "name from function";
    };
};

var $log = $("#log");
var base = new baseSettings();
var home = new homeSettings();

$log.html($log.html() + "<br /> baseSettings.message: '" + base.message + "'");
$log.html($log.html() + "<br /> baseSettings.getMessage(): '" + base.getMessage() + "'");
$log.html($log.html() + "<br /> homeSettings.name: '" + home.name + "'");
$log.html($log.html() + "<br /> homeSettings.getName(): '" + home.getName() + "'");
$log.html($log.html() + "<br /> homeSettings.message: '" + home.message + "'");
$log.html($log.html() + "<br /> homeSettings.getMessage(): '" + home.getMessage() + "'");

Results

baseSettings.message: 'base message'
baseSettings.getMessage(): 'base message from function'
homeSettings.name: 'name'
homeSettings.getName(): 'name from function'
homeSettings.message: 'base message'
homeSettings.getMessage(): 'base message from function'

Conclusion

I'm working through various examples I'm seeing on SO and I have to admit I'm not getting to a point where I understand why inheritance works or doesn't work in JS. Another problem I'm having is that in various tests the context for 'this' is changed, which I know happens, but I can't seem to get a good understanding of why or when so that I know exactly what's going on.

8
  • 1
    You're confusing constructor functions with prototype objects. Passing a constructor function to Object.create() is not really wrong, but it's strange and it won't work like you think. A constructor function is just a function; a prototype object is a plain object with properties that provide shared functionality, and generally that's what you pass to Object.create(). Commented Mar 1, 2016 at 13:55
  • this.getMessage = function sets the getMessage for each instance of the object individually on creation time. What you are looking for is to set baseSettings.prototype.getMessage = function outside of the constructor. Commented Mar 1, 2016 at 13:59
  • @Pointy Fantastic, my understanding is now back to square one! So what's the difference? How would you declare a prototype object and still have a constructor? In a lot of material that I've read they seem to be saying that a constructor function is essentially the equivalent to a class but acts as the constructor at the same time. This is why you can 'new' it up. I must admit, this is extremely frustrating! Commented Mar 1, 2016 at 14:01
  • @t.niese So there's no real inheritance there unless you declare that function on the prototype of the constructor. Will getMessage declared on the prototype still have access to 'message' declared in the constructor? Commented Mar 1, 2016 at 14:03
  • Well, your question is very broad, and a complete answer would comprise a couple of chapters in a book. I'll try and type in something simple on the basic topic of how a constructor function differs from a prototype object in providing heritable properties. Commented Mar 1, 2016 at 14:04

2 Answers 2

1

The key to inheritance in JavaScript is the prototype chain. Whenever a property lookup happens:

something.propertyName

the runtime first checks the object directly referenced. If the property is not found, then the process of checking the chain of objects (often it's just one object) linked by the prototype internal properties is searched.

Thus the key to making inheritance work is to create objects that have a prototype chain. The traditional way of doing that was to use the new operator and a constructor function:

function Constructor() {}
Constructor.prototype = {
  propertyName: function() { alert("hello world"); }
};

Now an object can be created:

var something = new Constructor();

and the property referenced:

something.propertyName(); // "hello world"

Note that this constructor function does not do anything it all. It could do something, but it fulfills its role in making inheritance work simply by existing and having that prototype object.

The Object.create() function is just another way of making an object with a prototype chain:

var something = Object.create({
  propertyName: function() { alert("hello world"); }
});

something.propertyName(); // "hello world"

That could also be written

var something = Object.create(Constructor.prototype);

to re-use the prototype object from the first example.

Of course, Object.create() makes a little more sense when the object to be used as the prototype is some object that's created in your code and kept around for that purpose, and not an on-the-fly object as in my example above. To use your test code:

var baseSettings = { // plain object, not a constructor
  message: "base message",
  getMessage: function() {
    return "base message: " + this.message;
  }
};

var homeSettings = Object.create(baseSettings, {
  name: "home name",
  getName: function() {
    return "home name: " + this.name;
  }
});

Now objects can be created with homeSettings as the prototype, and they'll inherit name and getName from that object, and message and getMessage from the baseSettings object:

var x = Object.create(homeSettings);
console.log(x.name); // "home name"
console.log(x.getName()); // "home name: home name"
console.log(x.message); // "base message"
console.log(x.getMessage()); // "base message: base message"

That could have been done with constructors instead of Object.create():

function HomeBase() {};
HomeBase.prototype = homeSettings;

var x = new HomeBase();

and the same sequence of console.log() calls would produce the same results. In either case — via new or via Object.create() — the object referenced in those examples by x has no properties of its own.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks this does help. I'm reading up on a few different patterns and realising that it's not as straight forward to achieve what I'm used to in C# I.e a 4 tier inheritance which has base, sub, subSub, subSubSub and base has properties that might be used by either subSub or subSubSub. My assumption is to use constructors and on instantiating subSubSub to call the super constructor for each level effectively passing the values up the chain until the base class instance.
@Jacques JavaScript inheritance is completely different than that in languages like C#, C++, or Java. It's much easier to completely forget about those languages while learning JavaScript. Honestly, I find that in practice inheritance and class structure is not a very big part of JavaScript programming at all.
Yes I get that from a lot of JS pros but when you have a decade of c# OOP coding behind you it's very difficult to let go of those concepts. Your mind automatically tries to map behaviours in the two languages together to understand the concepts better. The starting question for me is: As a language can JavaScript do X and Y (because I know this to be good OO principles). The next one is: How? The confusing part is where you see tons of 'Patterns' out there to achieve these concepts. This page has around 14 patterns hostmasterzone.info/combinationinheritance.html
0

The main problem in your code is this line:

homeSettings.prototype = Object.create(baseSettings);

You are passing a function constructor to Object.create when it expects just an object. In this case it will just return an empty object.

Using Object.create for inheritance is very simple.

//Base class
function Vehicle() {
  this.name = "vehicle " + parseInt( Math.random()*100 );
}

Vehicle.prototype.getName = function () {
  console.log( this.name );
};

//sub class
function Bike() {}

Bike.prototype = Object.create( Vehicle.prototype );
var honda = new Bike();

However, note that only prototype reference is set, constructor function Vehicle is never executed. Therefore the public properties defined inside base class are not copied over. Thus Bike class will have the start method but no name property.

This problem can be remedied to some extent by calling the Base class call method with context as subclass as shown below.

By calling the Base class call method inside the subclass we can copy over the properties from Base to Sub class.

However, the properties are copied per instance not on prototype so this requires more memory.

function Scooter() {
  //runs on creating every new instance so the name property is copied to each instance not to prototype
  Vehicle.call(this);
}

Scooter.prototype = Object.create( Vehicle.prototype );
var scooty = new Scooter();

Some more details at my github wiki page.

1 Comment

I agree with this approach and the explanation feels more at home considering I come from a c# background, but as I mentioned in my comment to Pointy's answer my problem is passing values through the constructors to various levels of sub/super instances because each layer makes use of them in different ways. One such property is called settings which is needed by the different tiers to extract values from input forms but is dependent on which form is visible at any point. It sounds convoluted but it worked really well up to a point and the code seemed really clean.

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.