3

To structure my JavaScript code I would like to use classes, the syntactical sugar for prototype inheritance new to ES6. I am having trouble though: it seems my prototype methods are not working as expected, which makes me feel I may have a fundamental misunderstanding of JavaScript classes, and/or how they work.

Take the following code, which declares a traditional constructor Cat and modifies its prototype, and an ES6 class Dog with a prototype method defined within it.

// Traditional Cat Constructor
// ===========================
function Cat(name) {
    this.name  = name;
    this.sound = "Meow.";
}

Cat.prototype.speak = function() {
    console.log(this.sound || "I don't make a sound.");
};


// Dog via ES6 Class
// =================
class Dog {
    constructor(name) {
        this.name = name;
        this.sound = "Woof!";
    }

    speak() {
        console.log(this.sound);
    }
}

var cat = new Cat("Bella");
var dog = new Dog("Sparky");

In NodeJS (tested with 4.4.2 and 6.0.0-pre), when I try to get the prototype of an instance of Dog, I get an empty object, but I am still able to call the prototype method on the instance.

Object.getPrototypeOf(cat);
// -> Cat { speak: [Function] }

Object.getPrototypeOf(dog);
// -> Dog {}

cat.speak();
// -> Meow.

dog.speak();
// -> Woof!

In Google Chrome, where I expect the result to be the same (since Node uses its engine), I instead get the expected result:

Object.getPrototypeOf(cat);
// -> Object {}
// ->     constructor: function Cat(name)
// ->     speak: function()
// ->     __proto__: Object

Object.getPrototypeOf(dog);
// -> Object {}
// ->     constructor: function(name)
// ->     speak: function()
// ->     __proto__: Object

I was getting an error in Node that said the prototype methods I defined in a class were not functions of instances of their classes, which is the only reason I noticed this, but unfortunately now it seems I cannot reproduce that issue.

Regardless, the above does not seams right, is there any explanation for why this code is behaving in this way?

JacksonHunt
  • 582
  • 6
  • 21

1 Answers1

10

Why are my class prototypes not working?

Only because the output is not how you expected doesn't mean it is not working. Neither the Node REPL nor console are standardized. Implementations are completely free to output whatever they want.

The only way to really know whether something "works" or not is to use it. So try to call dog.speak().

Regardless, the above does not seams right, is there any explanation for why this code is behaving in this way?

Class methods are non-enumerable properties, hence they don't show up in the Node output. Simplified example:

> var foo = {};
undefined
> Object.defineProperty(foo, 'bar', {value: 42, enumerable: false});
{}
> console.log(foo);
{}
undefined
> foo.bar
42
>

Regarding the Chrome output, it seems you expanded the object. Chrome always shows enumerable and non-enumerable properties.

> var foo = {};
  undefined
> Object.defineProperty(foo, 'bar', {value: 42, enumerable: false});
  Object {bar: 42}
> console.dir(foo);
  Object
    bar: 42
    __proto__: Object
  undefined

It also seems that the Chrome also shows non-enumerable properties in the summery. However, even though Chrome and Node share the same engine, they probably don't share the same logic for console or output generation.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143