- [2011-11-04] “Myth: JavaScript needs classes” introduces a more concise name for “prototypes as classes”: object exemplars. An exemplar is a factory for instances, constructors are function exemplars.
- [2012-01-08] “JavaScript inheritance by example” is a more hands-on introduction, complementing this post.
- [2012-07-29] Classes have been accepted for ECMAScript.next.
- [2012-10-03] Since this article has been written, it was decided that ECMAScript will have the special property __proto__ instead of the <| operator.
Incidentally, this post is also a good introduction to JavaScript inheritance, because the basics are easier to understand with prototypes as classes.
Table of contents:
0. Executive summary
1. Introduction
1.1. Prototypes
1.2. Prototypes as classes
1.3. Subclassing
1.4. Super-calls
2. Classes in JavaScript (ECMAScript 5)
3. Comparing JavaScript and NextScript
4. ECMAScript.next: ensuring compatibility with legacy code
4.1. Improved object literals in ECMAScript.next
5. A library for current JavaScript
6. Related reading
0. Executive summary
The core idea of “prototypes as classes” is that prototypes are better classes than constructor functions. That is, if you have a class C and create instances via new C(), then C should be the prototype of the instances, not their constructor function. Sect. 3 shows constructor functions as classes side by side with prototypes as classes, which should make it obvious which one is the better choice. Sect. 5 presents a library that lets you use prototypes as classes in today’s JavaScript.Read on for a gentle introduction to this idea.
1. Introduction
Prototypes as classes are explained by introducing a hypothetical programming language called “NextScript”. That language is exactly like JavaScript, with one difference: Where JavaScript uses the relatively complicated constructor functions to create instances, NextScript uses prototypes to do so. That is, prototypes are classes in NextScript. This post is structured as follows:- Sect. 1: Explain prototypes as classes via NextScript. First review the basics of prototypes (which are the same in JavaScript and NextScript). Then explain prototypes as classes in more detail.
- Sect. 2: For comparison, show how current JavaScript does classes and subclassing – via constructor functions as classes.
- Sect. 3: Show constructor functions as classes side by side with prototypes as classes to explain the benefits of the latter approach.
- Sect. 4: ECMAScript.next might get prototypes as classes, in a way that maximizes compatibility with legacy code. ECMAScript.next will also have more powerful object literals, which would benefit prototypes as classes.
- Sect. 5: The Proto.js library lets you use prototypes as classes in current JavaScript. Its succinctness is a partial validation of the approach.
1.1. Prototypes
The only inheritance mechanism that NextScript has is the prototype relationship between two objects. Prototypes work like in JavaScript:- The prototype chain: An object can optionally have a prototype, by pointing to another object via the internal property [[Prototype]]. The value of that property can be retrieved via Object.getPrototypeOf(). The prototype object can again have a prototype. The sequence of objects that are each other’s prototype is called the prototype chain.
- Property lookup: If NextScript cannot find a property in an object, it keeps looking in the prototype, then the prototype’s prototype etc. While it does so, this always points to the beginning of the prototype chain.
var obj = A <| { foo: 123 };
After the above assignment, obj has the prototype A. The equivalent in JavaScript is
var obj = Object.create(A);
obj.foo = 123;
Prototypes do everything. Both the instance-of relationship and the subclass-of relationship is expressed via the has-prototype relationship in NextScript:
- Instance and class, instance-of: the prototype object P of an object o plays the role of a class; o is an instance of P.
- Subclass and superclass, subclass-of: a prototype object P can have a superclass P' by making the object P' its prototype.
1.2. Prototypes as classes
When you think of a class as a construct that produces instances, then the closest thing to a class that JavaScript has are constructor functions (Sect. 2). In contrast, NextScript uses plain old (non-function) objects:- Classes: A class C is an object. It contains data that is shared by all instances (mainly methods). A special method called “constructor” sets up the instance-specific data (mainly data properties).
- Creating instances: Creating a new instance via new C(x,y) desugars to the following ECMAScript 5 code (internally):
var o = Object.create(C); o.constructor(x,y);That is, the following happens:- Create a new object o whose prototype is C.
- Call o.constructor(x,y)
- Instance check:
o instanceof Cchecks whether C is in the prototype chain of o. It is syntactic sugar forC.isPrototypeOf(o) - Getting the class: The class of an object is its prototype. Example: Are obj1 and obj2 (direct) instances of the same class?
Object.getPrototypeOf(obj1) === Object.getPrototypeOf(obj2)
var Person = {
constructor: function (name) {
this.name = name;
},
describe: function() {
return "Person called "+this.name;
}
};
Interaction:
> var john = new Person("John");
> john.describe()
Person called John
> john instanceof Person
true
1.3. Subclassing
A new class D extends an existing class C in two steps:- Inherit prototype properties: D’s prototype is C.
- Inherit instance properties: D.constructor() calls C.constructor() before setting up its own instance data.
Example: Subclass Worker extends superclass Person.
var Worker = Person <| {
constructor: function (name, title) {
Person.constructor.call(this, name);
this.title = title;
},
describe: function () {
return Person.describe.call(this)+" ("+this.title+")"; // (*)
}
};
Interaction:
> var jane = new Worker("Jane", "CTO");
> jane.describe()
Person called Jane (CTO)
> jane instanceof Worker
true
> jane instanceof Person
true
The instance jane has the prototype Worker which has the prototype Person. That is, prototypes are used for both the instance-of relationship and the subclass-of relationship. The following diagram shows this prototype chain:
1.4. Super-calls
Things become even simpler if we add one more feature: super-calls [4]. They make it easier for an overriding method to call the method it overrides (its super-method). There are two ways of looking up methods:- Normal method lookup: obj.m(). To find the method, we look for the first object in the prototype chain of obj that has a property m and invoke that property’s value. During this invocation, this is bound to obj, the object where the search began.
- Super-method lookup: super.l(). This invocation must be made from a method m. The search for l starts at the super-object of m (the prototype of the object that holds m). During the execution of l, this has the same value as it did in m.
var Super = {
foo: ...
};
var Sub = Super <| {
foo: function (x, y) {
super.foo(x, y); // (**)
}
};
The statement at (**) is a super-method lookup and syntactic sugar for
Object.getPrototypeOf(Sub).foo.call(this, x, y);
Now Worker can be simplified as follows.
var Worker = Person <| {
constructor: function (name, title) {
super.constructor(name);
this.title = title;
},
describe: function () {
return super.describe()+" ("+this.title+")";
}
};
2. Classes in JavaScript (ECMAScript 5)
Let’s look at how classes work in JavaScript.- Classes: A class is a constructor function C. C.prototype points to an object with the instance methods. C itself sets up the instance data properties.
- Creating instances: new C(x,y) does the following:
- Create a new object o whose prototype is C.prototype.
- Call C with this pointing to the newly created instance.
- Instance check: o instanceof C checks whether C.prototype is in the prototype chain of o.
- Getting the class: via the constructor property. Example: Are obj1 and obj2 (direct) instances of the same class?
obj1.constructor === obj2.constructor - Subclassing: A new constructor D extends an existing constructor C in two steps:
- Inherit prototype properties: Let D.prototype have the prototype C.prototype.
- Inherit instance properties: Let constructor D call constructor C as a function (no new operator!) and hand in this so that C adds its properties to the new instance of D (without creating a new instance itself).
// Superclass
function Person(name) {
this.name = name;
}
Person.prototype.describe = function() {
return "Person called "+this.name;
};
// Subclass
function Worker(name, title) {
Person.call(this, name);
this.title = title;
}
Worker.prototype = Object.create(Person.prototype);
Worker.prototype.constructor = Worker;
Worker.prototype.describe = function() {
return Person.prototype.describe.call(this)+" ("+this.title+")";
};
Note that super-calls are orthogonal to new-style inheritance and would be just as useful in the above code. The same holds for the prototype operator <|.
3. Comparing JavaScript and NextScript
The new approach allows you to work more directly with the core inheritance mechanism of JavaScript – prototypes. It thus continues the tradition of Crockford’s prototypal inheritance (Object.create() in ECMAScript 5). Several aspects of JavaScript become conceptually clearer with prototypes as classes/NextScript:- Being an instance versus having a prototype: In JavaScript, an instance o has two relationships with its class C: o is the instance of C and has the prototype C.prototype. In NextScript, there is only the prototype relationship between instance and class. As a result, instanceof becomes easier to understand.
JavaScript: o instanceof C === C.prototype.isPrototypeOf(o) NextScript: o instanceof C === C.isPrototypeOf(o) - Subclassing: In JavaScript, there is an indirection involved in subclassing. To let constructor D subclass constructor C, you must make D.prototype the prototype of C.prototype. In NextScript, you directly connect a subclass to its superclass. As a result, it is also easier to determine whether one class is a subclass of another one.
JavaScript: Sub.prototype = Object.create(Super.prototype) NextScript: Sub = Object.create(Super) - Checking for a superclass relationship: In JavaScript, a super-constructor and a sub-constructor are only related via the values of their prototype properties. Prototypes as classes are directly related.
JavaScript: Super.prototype .isPrototypeOf(Sub.prototype) NextScript: Super .isPrototypeOf(Sub) - Super-calls: When calling an overridden method in a superclass, you access the method in the super-prototype in JavaScript (i.e, not the superclass).
JavaScript: Super.prototype.foo.call(this) NextScript: Super.foo.call(this) - Inheriting class methods: In JavaScript, if a class has a method then a subclass does not inherit it. In NextScript, class methods are automatically inherited, due to the prototype relationship.
- Instantiation versus initialization: When it comes to creating a new instance, there are two concerns:
- Instantiation: Create a new instance, give it the proper prototype.
- Initialization: Set up the instance variables.
- Generic methods: To use a generic method, you have to refer to a prototype. The following example shows how to turn the pseudo-array arguments in an array via the slice() method of class Array.
JavaScript: Array.prototype.slice.call(arguments) NextScript: Array.slice.call(arguments)
4. ECMAScript.next: ensuring compatibility with legacy code
- Initial idea: My initial idea [1] is similar to the hypothetical NextScript as described above.
- Ensuring compatibility: Afterwards, Allen Wirfs-Brock suggested how things could be adapted so that the existing “class protocol” wouldn’t have to be changed [2]. This proposal might make it into ECMAScript.next.
Given a non-function object C (a “class object”, the prototype as a class):
- Make sure that C.constructor.prototype points to C. This step is needed for the new operator to work as described below.
- In the following two cases, treat non-function objects C differently, while not changing the behavior for functions:
- Interpret new C(...) as syntactic sugar for new C.constructor(...).
- Interpret o instanceof C as syntactic sugar for C.isPrototypeOf(o)
- Subclassing old-style classes: It might make sense to let a new-style class inherit from an old-style class. There are two ways to do this:
- Manually: The subclass extends Super.prototype. Constructor chaining and super-method calls should work as expected.
- Automatically: Extend the prototype operator <| so that, when it encounters a function f as its left-hand side, it makes f.prototype the prototype and not f.
- A competing proposal: Class literals have been proposed for ECMAScript.next. They look much like prototypes-as-classes, but are internally translated to constructor functions. It might be impossible to only have prototypes-as-classes in JavaScript (as that might break too much existing code). If so, then class literals avoid the hassle of having to support both prototypes-as-classes and constructor functions.
Alas, as things stand right now, it is not likely that prototypes as classes will ever make it into JavaScript. My current favorite are class literals that desugar to prototypes-as-classes, e.g.
class Foo extends Bar {
...
}
This would produce a prototype-as-class called Foo and the “class body” in curly braces would be very similar to an object literal. Class literals give you three benefits:
- It will be easier for IDEs to find your classes and support auto-completion etc.
- It gives you the option to introduce more inheritance-related features in the future (e.g. traits).
- They look familiar to people coming from class-based languages. Prototypes as classes ensure that the syntactic sugar is conceptually very similar to what is going on under the hood.
4.1. Improved object literals in ECMAScript.next
The proposal “Object Literal Extensions” has been accepted for ECMAScript.next. It is essential for making “prototypes as classes” easy to use. Highlights:- The prototype operator <| (borrowed by NextScript above).
- Super references (also borrowed by NextScript).
- A shorter notation for methods:
var obj = { data: "abc", mymethod(x, y) { ... } }; - Object literal shorthand: The following
function f(x, y) { return {x, y}; }is syntactic sugar forfunction f(x, y) { return {x: x, y: y}; }
5. A library for current JavaScript
The following code implements “prototypes as classes” in ECMAScript 5 and can be downloaded at proto-js on GitHub. Current JavaScript does not let you do prototypes-as-classes as shown above. Thus, the library uses methods instead of the following three operators (for which you cannot provide a custom implementation):- new operator – method new: Instead of
new MyClass(...)you writeMyClass.new(...)Credit for this idea goes to Irakli Gozalishvili and his prototype-centric inheritance library “selfish”. He comments:I think that the Proto.new(...) form is not a constraint. It’s a feature, as user may redefine their own custom “new”. I wish “new” and “instanceof” operators could be deprecated.
- <| operator – method extend: Instead of
A <| { ... }you writeA.extend({ ... })Subclasses of Proto automatically inherit its class method extend, because Proto is part of their prototype chain. - instanceof operator – method isPrototypeOf: Instead of
o instanceof Cyou writeC.isPrototypeOf(o)This is a built-in JavaScript method. One just exploits the fact that the prototype of an instance is also its class.
// To be part of ECMAScript.next
if (!Object.getOwnPropertyDescriptors) {
Object.getOwnPropertyDescriptors = function (obj) {
var descs = {};
Object.getOwnPropertyNames(obj).forEach(function(propName) {
descs[propName] = Object.getOwnPropertyDescriptor(obj, propName);
});
return descs;
};
}
/**
* The root of all classes that adhere to "the prototypes as classes" protocol.
* The neat thing is that the class methods "new" and "extend" are automatically
* inherited by subclasses of this class (because Proto is in their prototype chain).
*/
var Proto = {
/**
* Class method: create a new instance and let instance method constructor() initialize it.
* "this" is the prototype of the new instance.
*/
new: function () {
var instance = Object.create(this);
if (instance.constructor) {
instance.constructor.apply(instance, arguments);
}
return instance;
},
/**
* Class method: subclass "this" (a prototype object used as a class)
*/
extend: function (subProps) {
// We cannot set the prototype of "subProps"
// => copy its contents to a new object that has the right prototype
var subProto = Object.create(this, Object.getOwnPropertyDescriptors(subProps));
subProto.super = this; // for super-calls
return subProto;
},
};
Using the library:
// Superclass
var Person = Proto.extend({
constructor: function (name) {
this.name = name;
},
describe: function() {
return "Person called "+this.name;
}
});
// Subclass
var Worker = Person.extend({
constructor: function (name, title) {
Worker.super.constructor.call(this, name);
this.title = title;
},
describe: function () {
return Worker.super.describe.call(this)+" ("+this.title+")";
}
});
Interaction:
var jane = Worker.new("Jane", "CTO"); // normally: new Worker(...)
> Worker.isPrototypeOf(jane) // normally: jane instanceof Worker
true
> jane.describe()
'Person called Jane (CTO)'
6. Related reading
- Classes: suggestions for improvement [Initial idea to allow new for non-function objects]
- Prototypes as the new class declaration [Proposal for ensuring the compatibility with the current protocol]
- What’s up with the “constructor” property in JavaScript?
- Harmony: Object Literal Extensions
- Lightweight JavaScript inheritance APIs [Especially Resig’s Simple Inheritance looks almost like NextScript]
- A brief history of ECMAScript versions (including Harmony and ES.next)




15 comments:
"Make sure that C.constructor.prototype points to C."
=> Didn't you rather mean "C.prototype.constructor"?
No, the “class object” C has the constructor function as a method. That constructor function must have a property “prototype” that points to the prototype (which is C). After you have done that, new C.constructor(...) works properly. I’ve added a clarifying sentence.
Alex while moving with my library forward https://github.com/Gozala/selfish
I've came to conclusion that `.constructor` is not really necessary, specially since it requires special prototype property to be set. Instead I just use `initialize` method to init properties. Also I've changed extend a bit to allow object compositions.
And BTW I think that Proto.new(...) form is not a constraint it's a feature as user may redefine their own custom `new`. I wish `new` and `instanceof` operators could be deprecated.
Good point. If you are not obsessed about compatibility then “initialize” much better expresses what that method does.
I agree.
pretty awesome.
in the paragraph
'Subclassing: In JavaScript, there is an indirection involved in subclassing. To let constructor D subclass constructor C, you must make D.prototype the prototype of C.prototype.'
I'm confused as to what you mean by 'the prototype of C.prototype' - isn't C.prototype the 'class' in this model...? so where is 'the prototype of C.prototype'
Thanks if you can clarify for me.
In class-based languages there are two inheritance relationships:
- instance-of: an instance “inherits” the methods of a class (and more).
- subclass-of: a subclass inherits the structure of its superclass.
In JavaScript, the prototype-of relationship between objects is the only inheritance mechanism. D.prototype is indeed the “class” of D instances, but we also want D instances to have what C.prototype provides. Therefore, the object in D.prototype also has a prototype (namely, C.prototype). The prototype chain of a D instance in ECMAScript 5 therefore looks as follows:
D instance -> D.prototype -> C.prototype
Does that make things clearer? The key thing is not to confuse the prototype relationship between objects and the prototype property of constructors. More information: Section 2 and “JavaScript inheritance by example” [1].
[1] http://www.2ality.com/2012/01/js-inheritance-by-example.html
Thank you Alex.
I still only understand this:
"[...] To let constructor D subclass constructor C, you must make D.prototype the prototype of C.prototype."
"To let constructor D subclass constructor C, you must make D.prototype C.prototype" ???
no? sorry bit rushed, will check the link you included. Thank you for the article and the resonses.
function D() { ... }
// C.prototype becomes the prototype of D.prototype
D.prototype = Object.create(C.prototype);
D.prototype.foo = function () { ... };
Thxs for article. I want to share how I implement classes in my projects finally:
https://sites.google.com/site/javascriptclasses/
Hi,
i´m web designer and js enthusiast for many years. It looks to me that
the long tale of inheritance in javascript is approaching a happy end.
Your solution seems to be sharp, simple, elegant, and it remains just
javascript. People is wishing different things in a language, some want
classes for example. But I was just wishing for a nice simple way to
modularize my code. Thx.
Hello all,I am new and I would like to ask that what are the benefits of sql training,What all topics should be covered in it?
And has anyone studied from this course http://www.wiziq.com/course/2118-learn-how-to-program-in-c-language of C tutorial online?? or tell me any other guidance...
would really appreciate help
Post a Comment