What benefits does ES2015 (ES6) `class` syntax provide?
The new class
syntax is mostly, though not entirely, syntactic sugar (but, you know, the good kind of sugar). It markedly simplifies writing constructor functions and the objects they assign as prototypes to the objects they create, especially when setting up inheritance hierarchies, which was error-prone with the ES5 syntax. But unlike the old way, class
syntax also enables super.example()
for supercalls (which are notoriously hard to do the old way) as well as property declarations, private fields, and private methods (including static ones).
(Sometimes people say you have to use class
syntax if you want to subclass Error
or Array
[which couldn't be properly subclassed in ES5]. That's not true, you can use a different ES2015 feature, Reflect.construct
[spec, MDN], if you don't want to use class
syntax.¹)
Moreover, is
class
a different kind of OOP or it still JavaScript's prototypical inheritance?
It's the same prototypical inheritance we've always had, just with cleaner, more convenient, and less error-prone syntax if you like using constructor functions (new Foo
, etc.), plus some added features.
Can I modify it using
.prototype
?
Yes, you can still modify the prototype
object on the class's constructor once you've created the class. E.g., this is perfectly legal:
class Foo {
constructor(name) {
this.name = name;
}
test1() {
console.log("test1: name = " + this.name);
}
}
Foo.prototype.test2 = function() {
console.log("test2: name = " + this.name);
};
Are there speed benefits?
By providing a specific idiom for this, I suppose it's possible that the engine may be able to do a better job optimizing. But they're awfully good at optimizing already, I wouldn't expect a significant difference. One thing in particular about class
syntax is that if you use property declarations, you can minimize the number of shape changes an object goes through when being constructed, which can make interpreting and later compiling the code a bit faster. But again, it's not going to be big.
What benefits does ES2015 (ES6)
class
syntax provide?
Briefly: If you don't use constructor functions in the first place, preferring Object.create
or similar, class
isn't useful to you.
If you do use constructor functions, there are some benefits to class
:
The syntax is simpler and less error-prone.
It's much easier (and again, less error-prone) to set up inheritance hierarchies using the new syntax than with the old.
class
defends you from the common error of failing to usenew
with the constructor function (by having the constructor throw an exception).Calling the parent prototype's version of a method is much simpler with the new syntax than the old (
super.method()
instead ofParentConstructor.prototype.method.call(this)
orObject.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)
).Property declarations can make the shape of the instances being created clearer, separating it from the constructor logic.
You can use private fields and methods (both instance and static) with
class
syntax, and not with ES5 syntax.
Here's a syntax comparison (without private members) for a hierarchy:
// ***ES2015+**
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
personMethod() {
// ...
}
}
class Employee extends Person {
constructor(first, last, position) {
super(first, last);
this.position = position;
}
employeeMethod() {
// ...
}
}
class Manager extends Employee {
constructor(first, last, position, department) {
super(first, last, position);
this.department = department;
}
personMethod() {
const result = super.personMethod();
// ...use `result` for something...
return result;
}
managerMethod() {
// ...
}
}
Example:
// ***ES2015+**
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
personMethod() {
return `Result from personMethod: this.first = ${this.first}, this.last = ${this.last}`;
}
}
class Employee extends Person {
constructor(first, last, position) {
super(first, last);
this.position = position;
}
personMethod() {
const result = super.personMethod();
return result + `, this.position = ${this.position}`;
}
employeeMethod() {
// ...
}
}
class Manager extends Employee {
constructor(first, last, position, department) {
super(first, last, position);
this.department = department;
}
personMethod() {
const result = super.personMethod();
return result + `, this.department = ${this.department}`;
}
managerMethod() {
// ...
}
}
const m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops");
console.log(m.personMethod());
Are ES6 classes just syntactic sugar for the prototypal pattern in Javascript?
Yes, perhaps, but some of the syntactic sugar has teeth.
Declaring a class creates a function object that is the constructor for the class, using the code provided for constructor
within the class body, and for named classes, with the same name as the class.
The class constructor function has a normal prototype object from which class instances inherit properties in normal JavaScript fashion. Instance methods defined within the class body are added to this prototype.
ES6 does not provide a means to declare class instance default property values (i.e. values which are not methods) within the class body to be stored on the prototype and inherited. To initialize instance value you can either set them as local, non inherited properties within the constructor, or manually add them to the class constructor's prototype
object outside the class definition in the same fashion as for ordinary constructor functions. (I am not arguing the merits or otherwise of setting up inherited properties for JavaScript classes).
Static methods declared within the class body are added as properties of the class constructor function. Avoid using static class method names that compete with standard function properties and methods inherited from Function.prototype
such as call
, apply
or length
.
Less sugary is that class declarations and methods are always executed in strict mode, and a feature that gets little attention: the .prototype
property of class constructor functions is read only: you can't set it to some other object you've created for some special purpose.
Some interesting stuff happens when you extend a class:
the
prototype
object property of the extended class constructor is automatically prototyped on theprototype
object of the class being extended. This is not particularly new and the effect can be duplicated usingObject.create
.the extended class constructor function (object) is automatically prototyped on the constructor function of the class being extended, not
Function
. While it may be possible to replicate the effect on an ordinary constructor function usingObject.setPrototypeOf
or evenchildClass.__proto__ = parentClass
, this would be an extremely unusual coding practice and is often advised against in JavaScript documentation.
There are other differences such as class objects not being hoisted in the manner of named functions declared using the function
keyword.
I believe it could be naive to think that Class declarations and expressions will remain unaltered in all future versions of ECMA Script and it will be interesting to see if and when developments occur. Arguably it has become a fad to associate "syntactical sugar" with classes introduced in ES6 (ECMA-262 standard version 6) but personally I try to avoid repeating it.
When/Why Use Class in JavaScript Over a Constructor?
JavaScript classes, introduced in ECMAScript 2015, are primarily
syntactical sugar over JavaScript's existing prototype-based
inheritance. The class syntax does not introduce a new object-oriented
inheritance model to JavaScript.
For more defailts, see MDN
Are ES6 class private properties just syntactic sugar?
Are ES6 class private properties just syntactic sugar?
No. They're a fundamental addition to how objects work at an internal level. Private fields (as they're called) are held in new slots in the object that didn't exist before the proposal and are not accessible in other ways.
So I would like to understand if '#' syntax for private properties in ES6 classes is syntactic sugar and can be polyfilled in some way for function lovers like myself.
You can't use private properties without class
syntax. (Future proposals may change that.) Instead, you'd have to keep doing what you're doing (the closure solution) or use a WeakMap
only your functions have access to keyed by the object the properties relate to.
You've done your own closure examples, so here's your Person
class using the WeakMap
approach instead of private properties:
const Person = (() => {
const names = new WeakMap();
function Person(name) {
names.set(this, name);
}
Person.prototype.getName = function getName() {
return names.get(this);
};
return Person;
})();
let ron = new Person("ron")
console.log(ron.name); // undefined
console.log(ron.getName()); // "ron"
Why would the ES6 class syntax console log differently than an ES5 class syntax?
It's because methods defined in ES6 classes are non enumerable. In a Man object, isRunning is enumerable, but not in Woman. And Chrome has a specific way of handling the console log. Depending on the presence of enumerable properties or not will affect the display.
The difference in the console display is trivial, but it shows an interesting difference in the way classes are built with ES6 Class. To see it more clearly, you can try to either make Man isRunning non enumerable or Woman isRunning enumerable, it should give the same output in the console. Like this for non enumerable in Man:
var Man = (function() { "use strict";
function Man() { this.run = true }
Object.defineProperty(Man.prototype, 'isRunning', { value: function() { console.log('yesss imma run'); }, enumerable: false });
return Man; })();
bob = new Man(); console.log(bob);
class Woman { constructor() { this.run = true; }
isRunning() { console.log('yess imma run'); } }
daisy = new Woman(); console.log(daisy);
when to use class/object constructor
class
syntax is mostly (but not entirely) syntactic sugar for creating constructor functions and filling in the object on their prototype
property. You can do almost everything class
does writing constructor functions explicitly and adding properties to their prototype
property and using some methods on the ES2015+ Reflect
object, but it's doing so is much more verbose and error-prone. (The number of things that you can only do with class
syntax is going to increase before too long as the private fields and methods proposals mature.) I've given an overview of how class
syntax can be emulated with earlier constructs in my answer to What benefits does ES2015 (ES6) class
syntax provide? here.
As to when to use which, it's mostly a matter of style for you and your team to decide. Even for the things that you can't literally do without class syntax (such as private fields, which will likely advance to the living specification at some point this year and be in ES2021), you can do similar things in other ways.
But:
- If what you're doing requires
class
syntax (for instance, if you want to use private fields, once they're added [or now via transpilation]) and you want to do that thing specifically rather than something else that's similar to it, then you'll need to useclass
syntax. - If not, it's largely a matter of style for you and your team to decide.
In ES6 class syntax why setPrototypeOf() work and other similar method don't
When you use the class
syntax, the prototype
property of the resulting constructor function is not writable and not configurable. That means you cannot assign a new value to it, neither via an assignment expression nor via Object.defineProperty
.
class Book {}
console.log(Object.getOwnPropertyDescriptor(Book, 'prototype'));
Related Topics
Add Event Listener on Elements Created Dynamically
Prevent a Webpage from Navigating Away Using JavaScript
What Does [].Foreach.Call() Do in JavaScript
JavaScript "Addeventlistener" Event Fires on Page Load
How to Sort Elements by Numerical Value of Data Attribute
What Is the Correct Way to Write HTML Using JavaScript
Html5 Canvas Ctx.Filltext Won't Do Line Breaks
Performance Differences Between Visibility:Hidden and Display:None
Formdata.Append("Key", "Value") Is Not Working
Get Mouse Wheel Events in Jquery
Parse Date Without Timezone JavaScript
How to Query Mongodb Objectid by Date
What Is "Undefined X 1" in JavaScript
Form Submit Execute JavaScript Best Practice