Krizzu Krizzu - 29 days ago 8
Javascript Question

How much ES6 classes differ from ES5 style?

I'm that kind of a person who needs to know everything in depth... So, I've been going through many taught subjects and I set my foot into depths of Prototype-inheritance.

I have a clear vision of how it works in ES5 (Every function has this special prototype property, which points to an object on which it is based on. This object has .constructor property, which points back to the function etc.).

So, now let's see ES5 example:

function Bunny(name) {
this.name = name
}

Bunny.prototype.sayName = function() {
console.log('Im',this.name)
}


This one is pretty clear: function Bunny gets argument
name
which's going to be assign to a new object.

Next line adds function to the function's prototype, which's going to return current name.

Let's see ES6 class now:

class Fox{
constructor(name){
this.name = name;
}

sayName() {
console.log('Im', this.name)
}
}


Same stuff here:
Constructor
here is like our Bunny function. But
sayName
in fox is not the same as
sayName
in Bunny.

Let's create the instances:

let bunny = new Bunny('Henry');
let fox = new Fox('Jerry');


And now, check their prototypes:

console.log(Object.getPrototypeOf(bunny))
console.log(Object.getPrototypeOf(fox))


What do we get?

//using repl.it - ES6
{ sayName: [Function] }
{}


Why is that?

I thought it might be because we set function
sayName
on Bunny's prototype directly. So I've changed it to this:

function Bunny(name) {
this.name = name

//Warning - Bad practice ahead!
this.sayName = function() {
console.log('Im',this.name)
}
}


Result:

//using repl.it - ES6
{}
{}


That would have sense, if not this:

console.log(bunny.hasOwnProperty('sayName'))
console.log(fox.hasOwnProperty('sayName'))


Which means,
fox
does not own
sayName
on him, either prototype shows it has it. Am I missing something here? Why They are different?

Answer

In ES6 classes all methods are non-enumerable, so you when you log a prototype of an instance of an ES6 class, you get something that looks like an empty object.

See this example:

const obj = new (class {method() {}});

console.log(Object.getPrototypeOf(obj)); // {}
console.log(typeof Object.getPrototypeOf(obj).method); // function

In ES5 you define a method by assigning it to a property of the class prototype, which makes it enumerable. If you wanted to achieve the same effect as with ES6 classes, you could use Object.defineProperty() instead:

const TestClass = function TestClass() {};
Object.defineProperty(TestClass.prototype, 'method', {
  value: function() {},
  writable: true,
  enumerable: false,
  configurable: true,
});
const obj = new TestClass();

console.log(Object.getPrototypeOf(obj)); // {}
console.log(typeof Object.getPrototypeOf(obj).method); // function


And fox.hasOwnProperty('sayName') returns false because hasOwnProperty() checks only for own properties, and sayName is in the prototype chain. If you want to check for properties in the prototype chain too, you can use the in operator: 'sayName' in fox returns true.


See also Enumerability and ownership of properties on MDN.