var jane = {
name: "Jane",
describe: function() {
return "Person called "+this.name;
}
};
console.log(jane.describe()); // Person called Jane
jane is an object which has been created via an object initializer. name and describe are properties. describe is a property whose value is a function. Such function-valued properties are called methods. An object initializer looks like a dictionary (map), but it is a real object. In most class-based languages, you need a class to create one. Hence the singleton pattern.
The core idea of prototypal inheritance is incredibly simple, simpler than classes. What makes JavaScript so complicated is that this core idea is obscured by trying to make the creation of instances (of a given type) look like Java. The core idea of prototypal inheritance is: an object can point to another object and thus make it its prototype. If a property isn’t found in the object, the search continues in the prototype (and, if it has one, its prototype, etc.). That allows one to model jane and tarzan as “instances” of PersonProto:
var PersonProto = {
describe: function () {
return "Person called "+this.name;
},
};
var jane = {
__proto__: PersonProto,
name: "Jane",
};
var tarzan = {
__proto__: PersonProto,
name: "Tarzan",
};
console.log(jane.describe()); // Person called Jane
jane and tarzan share the same prototype PersonProto which provides method describe() to both of them. Note how similar PersonProto is to a class.
Above, we have set up jane and tarzan manually, an exemplar is a factory for instances. In class-based languages, classes are exemplars. In JavaScript, the standard exemplars are constructors (functions). But one can also use objects as exemplars. The following sections explain both kinds of exemplars.
// Constructor: set up the instance
function Person(name) {
this.name = name;
}
// Prototype: shared by all instances
Person.prototype.describe = function () {
return "Person called "+this.name;
};
var jane = new Person("Jane");
console.log(jane instanceof Person); // true
console.log(jane.describe()); // Person called Jane
function Employee(name, title) {
Person.call(this, name);
this.title = title;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.describe = function () {
return Person.prototype.describe.call(this)
+ " (" + this.title + ")";
};
var jane = new Employee("Jane", "CTO");
console.log(jane instanceof Person); // true
console.log(jane instanceof Employee); // true
console.log(jane.describe()); // Person called Jane (CTO)
There is no doubt that that is unwieldy (details are explained here). As a result, numerous libraries for handling inheritance have been written for JavaScript. But JavaScript needs a built-in solution. A minimal way of helping developers is to add language features that simplify the most common inheritance tasks. The next section describes four candidates.
It is interesting to note that even with subtyping, instances still are easy to understand: The following is the structure of the instance jane.
![]() |
| Person.prototype is the prototype of instances of Person, Employee.prototype is the prototype of instances of Employee (such as jane). By making Person.prototype the prototype of Employee.prototype, you let Employee inherit the methods of Person. |
Class-based languages have two relationships:
var Super = function () { ... }
var Sub = Super <| function () { ... }
The constructor Sub extends the constructor Super.
super.describe()
var colorPoint = { color: "green" };
colorPoint .= { x: 33, y: 7 }
Afterwards, colorPoint has the value
{ color: "green", x: 33, y: 7 }
{
method(arg1, arg2) {
...
}
}
is an abbreviation for
{
method: function (arg1, arg2) {
...
}
}
function Person(name) {
this.name = name;
}
Person.prototype .= {
describe() {
return "Person called "+this.name;
}
};
var Employee = Person <| function (name, title) {
super.constructor(name);
this.title = title;
}
Employee.prototype .= {
describe() {
return super.describe() + " (" + this.title + ")";
}
};
That looks much nicer, doesn’t it?
var Person = {
constructor(name) {
this.name = name;
},
describe() {
return "Person called "+this.name;
}
};
var Employee = Person <| {
constructor(name, title) {
super.constructor(name);
this.title = title;
},
describe() {
return super.describe() + " (" + this.title + ")";
}
};
var jane = new Employee("Jane", "CTO");
console.log(jane instanceof Employee); // true
So we have flipped things: The previous prototypes Person.prototype and Employee.prototype are now the “classes” Person and Employee. And the previous constructor functions Person() and Employee() are now methods Person.constructor() and Employee.constructor(). Details are explained at [1].
For the above to work, you only need to adapt the following things:
Constructor1 <| Constructor2
Prototype1 <| Prototype2
Constructor <| Prototype
Prototype <| Constructor
Advantage. The main advantage of object exemplars is that they are a good fit for prototypal inheritance, because you can work directly with prototypes. In contrast, with constructors, there is always an indirection after the creation of an instance: You now always need the constructor to access the prototype. The following examples demonstrate this difference between function exemplars and object exemplars:
| Func. ex.: | o instanceof E === E.prototype.isPrototypeOf(o) |
| Obj. ex.: | o instanceof E === E.isPrototypeOf(o) |
| Func. ex.: | Sub.prototype | = Object.create(Super.prototype) |
| Obj. ex.: | Sub | = Object.create(Super) |
| Func. ex.: | Super.prototype | .isPrototypeOf(Sub.prototype) |
| Obj. ex.: | Super | .isPrototypeOf(Sub) |
| Func. ex.: | Array.prototype.slice.call(arguments) |
| Obj. ex.: | Array.slice.call(arguments) |
class Person {
constructor(name) {
this.name = name;
},
describe() {
return "Person called "+this.name;
}
}
class Employee extends Person {
constructor(name, title) {
super.constructor(name);
this.title = title;
},
describe() {
return super.describe() + " (" + this.title + ")";
}
}
JavaScript has a long history of proposals for class-like syntactic constructs. The proposal that might be added to ECMAScript.next is called “Maximally Minimal Classes”. But it is still hotly debated.
Advantages of class declarations: