I\'ve been instantiating subclasses in javascript using
object = new class ()
but I notice some people instantiate using
ob
The difference is that when you do:
var subclass = new superclass();
you are creating an instance of superclass
. subclass
is just variable. You are not creating a sub-class (ie. making subclass
inherit superclass
).
In the latter example, assuming subclass is a function, you are saying that all new instances of subclass should inherit (ie. sub-class) superclass
.
So:
function superclass() {this.stuff="stuff";}
function subclass() {}
subclass.prototype = new superclass();
alert(new subclass().this); // pops up "stuff"
is prototypical inheritance.
As for the new
operator, it is used for creating an instance of built-in objects and user defined types. A user defined type is simply a function.
Edit:
When I wrote above that subclass inherits supertype using prototypical inheritance, I mean that all new instances of subclass inherit from one particular instance of superclass, not from the superclass
type/function itself.
Sharing a quick demo of Javascript inheritance after reading the mozilla doc
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "";
}
function Programmer (name, projs) {
Employee.call(this, name, "programming");
this.projects = projs || [];
}
Programmer.prototype = new Employee;
// demo dynamic inheritance
Employee.prototype.leave = 10;
var johnny = new Programmer("Johnny", ["C#","Java"]);
alert("name: " + johnny.name + "\n"
+ "dept: " + johnny.dept + "\n"
+ "projects: " + johnny.projects + "\n"
+ "leave: " + johnny.leave);
var mary = new Programmer("Mary", ["Javascript","Java"]);
alert("name: " + mary.name + "\n"
+ "dept: " + mary.dept + "\n"
+ "projects: " + mary.projects + "\n"
+ "leave: " + mary.leave);
alert("changing leave of all staff to 8");
Employee.prototype.leave = 8;
alert("Johnny leave: " + johnny.leave); // 8
alert("Mary leave: " + mary.leave); // 8
alert("cannot batch move staff to another department");
Employee.prototype.dept = "sales";
alert("Johnny dept: " + johnny.dept); // programming
alert("Mary dept: " + mary.dept); // programming
Demo in jsfiddle Demo with more debug in jsfiddle
The code samples in your question reflect a couple of misunderstanding. Let's address them first:
Quoting Douglas Crockford in Chapter 5, Inheritance, of JavaScript: The Good Parts:
Instead of having objects inherit directly from other objects, an unnecessary level of indirection is inserted such that objects are produced by constructor functions.
(...)
When a function is invoked with the constructor invocation pattern using the new prefix, this modifies the way in which the function is executed.
Douglas Crockford then explains how the new operator could be implemented as a JavaScript function. This function makes use of several other functions defined in the book, so I rewrote it in a (somewhat) simpler form below:
function createNew(constructor) {
// a function to explain the new operator:
// var object = createNew(constructor);
// is equivalent to
// var object = new constructor();
//
// param: constructor, a function
// return: a new instance of the "constructor" kind of objects
// step 1. create a new empty object instance
// linked to the prototype of provided constructor
var hiddenLink = function(){};
hiddenLink.prototype = constructor.prototype;
var instance = new hiddenLink(); // cheap trick here: using new to implement new
// step 2. apply the constructor the new instance and get the result
var result = constructor.apply(object); // make this a reference to instance within constructor
// step 3. check the result, and choose whether to return it or the created instance
if (typeof result === 'object') {
return object;
} else {
return instance;
}
}
In simple English, if you call new constructor()
, where constructor is a function, the operator creates a new object with a link to inherit properties from the constructor, applies the constructor function to it, and returns either the value returned by the constructor, or the new object in case the constructor returned something else which is not an object.
At any time, before or after creating new instances using a custom constructor, you may modify the prototype (object) on the constructor (function):
function constructor(){} // the most simple constructor function: does nothing
var before = new constructor();
var different = new constructor();
different.answer = "So long, and thanks for all the fish";
constructor.prototype = {}; // set an empty object to the prototype property
constructor.prototype.answer = 42; // create a new property on prototype object
constructor.prototype.answer = Math.PI; // replace an existing property
var after = new constructor();
Through the hidden link added to all objects created using this constructor (see "cheap trick" in createNew), the properties of the prototype object can be accessed on all these instances, unless overridden by properties defined on the objects directly.
before.answer === Math.PI; // true
after.answer === Math.PI; // true
different.answer === "So long, and thanks for all the fish"; // true
Using this newly acquired knowledge, how would you create a new "class" of objects that inherit all the properties of arrays, together with a new method empty() to remove all elements?
First, there are no classes in Javascript, so in order to create a new "class", I have to define a new constructor function. Let's call it CustomArray, with a capital C to follow the convention that constructor functions should start with a capital.
function CustomArray(){}
I can now create custom instances:
var myArray = new CustomArray();
Second, I want instances created with CustomArray to inherit Array properties:
myArray.prototype = new Array(); // WRONG EXAMPLE: we must set CustomArray.prototype
CustomArray.prototype = Array; // WRONG EXAMPLE: prototype expects an object, Array is a function
CustomArray.prototype = new Array(); // OK, BUT: the simpler form [] should be used instead
CustomArray.prototype = [];
Third, I want all instances created with CustomArray to have the empty() method:
function empty(){
// empty this array by setting its length to 0
// function to be called in the context (this) of an array
this.length = 0;
}
CustomArray.prototype.empty = empty; // set the function named empty to the property "empty" of the prototype
Finally, I can rewrite the whole example in a more concise way:
function CustomArray(){}
CustomArray.prototype = [];
CustomArray.prototype.empty = function(){ this.length = 0; }
I can now create a custom array, set a couple of values and empty it:
var myArray = new CustomArray();
myArray[0] = "abc";
myArray[1] = "def";
myArray[2] = "ghi";
myArray.empty();
The issue is: the above code does not work as expected. Why? Because unlike in regular arrays, setting values in our custom array does not automagically increase the length property of the array. Likewise, calling empty() only sets the length property of our custom array to 0, it does not delete all the values within.
Besides, we could not use the array literal syntax to initialize our custom array:
var myArray = ["abc","def","ghi"]; // this creates a regular array
All in all, it is important to understand how Javascript inheritance works, but you may often find it less useful than expected and there are simpler ways to achieve the same result, for example by using builder functions instead of constructors. We can solve the problem by using a builder function customizeArray to extend regular arrays:
function customizeArray(array){
array.empty = function(){
this.length = 0;
};
}
var myArray = ["abc","def","ghi"];
customizeArray(myArray);
myArray.empty();
This code works as expected because myArray is a regular array in this case, extended with a new method named empty. The main advantage and drawback of this approach in comparison with using prototypes is that it modifies only the selected instance, and if you were working with lots of similar objects at the same time, setting this extra property would use more memory than setting a single shared property on a common prototype.
To avoid that, you may be tempted to modify the Array prototype directly:
Array.prototype.empty = function(){
this.length = 0;
};
var myArray = ["abc","def","ghi"];
myArray.empty();
It works, but I would advise against it: you are attaching a custom property to every array instance, including all those created by those fancy libraries, plugins, frameworks, advertising and analytics scripts, all the code on your page. Needless to say that it may break something in a place where you cannot fix it.
Edit: Interesting post on kangax's blog as a follow-up: "How ECMAScript 5 still does not allow to subclass an array"