What is the reason to use the 'new' keyword at Derived.prototype = new Base
What does the following code do:
WeatherWidget.prototype = new Widget;
where Widget
is a constructor, and I want to extend the Widget 'class' with a new function WeatherWidget
.
What is the new
keyword doing there and what would happen if it is left out?
Solution 1:
WeatherWidget.prototype = new Widget;
The new
keyword calls Widget
as a constructor and the return value is assigned to the prototype
property. (If you would omit new
, you would not call Widget
unless you added an argument list, ()
. However, calling Widget
that way might not be possible. It would certainly have the potential to spoil the global namespace if it is not strict mode code and the implementation is conforming to ECMAScript Ed. 5.x there, because then this
in the constructor would refer to ECMAScript’s global object.)
But this approach actually comes from a really viral bad example in the old Netscape JavaScript 1.3 Guide (mirrored at Oracle, formerly Sun).
This way, your WeatherWidget
instances will all inherit from the same Widget
instance. The prototype chain will be:
[new WeatherWidget()] → [new Widget()] → [Widget.prototype] → …
This can be useful, but most of the time you would not want it to happen. You should not do that here unless you want all your WeatherWidget
instances to share among them the property values they inherit from this Widget
instance, and only through it, from Widget.prototype
. Another problem is that you need to call the parent constructor this way, which may not allow to be called without arguments as you do, or would not initialize properly. It certainly has nothing to do with emulation of class-based inheritance as known, e.g., from Java.
The proper way to implement class-based inheritance in these prototype-based languages is (originally devised by Lasse Reichstein Nielsen in comp.lang.javascript
in 2003, for cloning objects):
function Dummy () {}
Dummy.prototype = Widget.prototype;
WeatherWidget.prototype = new Dummy();
WeatherWidget.prototype.constructor = WeatherWidget;
The constructor
prototype property should be fixed as well, so that your WeatherWidget
instances w
would have w.constructor === WeatherWidget
as expected, and not w.constructor === Widget
. However, be aware that it is enumerable afterwards.
This way, WeatherWidget
instances will inherit properties through the prototype chain, but will not share property values among them, because they inherit from Widget.prototype
through Dummy
which has no own properties:
[new WeatherWidget()] → [new Dummy()] → [Widget.prototype] → …
In implementations of ECMAScript Ed. 5 and later, you can and should use
WeatherWidget.prototype = Object.create(Widget.prototype, {
constructor: {value: WeatherWidget}
});
instead. This has the additional advantage that the resulting constructor
property is not writable, enumerable, or configurable.
The parent constructor will only be called if you call it explicitly, from WeatherWidget
, for example with
function WeatherWidget (…)
{
Widget.apply(this, arguments);
}
See also Function.prototype.extend()
in my JSX:object.js for how to generalize this. Using that code, it would become
WeatherWidget.extend(Widget);
My Function.prototype.extend()
takes an optional second argument with which you can easily augment the prototype of WeatherWidget
instances:
WeatherWidget.extend(Widget, {
foo: 42,
bar: "baz"
});
would be equivalent to
WeatherWidget.extend(Widget);
WeatherWidget.prototype.foo = 42;
WeatherWidget.prototype.bar = "baz";
You will still need to call the parent constructor explicitly in the child constructor, though; that part cannot reasonably be automated. But my Function.prototype.extend()
adds a _super
property to the Function
instance which makes it easier:
function WeatherWidget (…)
{
WeatherWidget._super.apply(this, arguments);
}
Other people have implemented similar extensions.
Solution 2:
According to some odd Javascript rules, new Widget
actually invokes the constructor rather than returning a reference to the constructor. This question actually answers the question the difference between var a = new Widget()
and var a = Widget()
.
In simple words, the new
keyword tells Javascript to call the function Widget
under a different set of rules than a regular function call. Going off the top of my head, the ones I remember are:
- There is a brand new object created
-
Widget
can use thethis
keyword to refer to that object. - If
Widget
does not return anything, this new object will be created. - This object will inherit a few additional properties that will indicate it was created by
Widget
that are used to track down property chains.
Without the new
keyword, a call to widget would
- If in strict mode,
this
will be set toundefined.
- Otherwise,
this
will refer to the global object. (Calledwindow
by the browser.) - If the function does not return anything, then
undefined
will be returned.
Reference:
new
keyword
Solution 3:
WeatherWidget.prototype = new Widget;
does create a new instance of the Widget
constructor and use it as WeatherWidget
's prototype object. Using the new
keyword creates the new object, sets up the inheritance chain of it to Widget.prototype
, and applies the constructor function on it (where you can set up individual properties'n'methods, or create private-scoped variables).
Without the new
keyword it would be an assignment of the Widget
function to the prototype
property - which does not make any sense. If you'd add the optional brackets (i.e. Widget()
), it would invoke the function normally, but not as a constructor on a new instance, but with the global object as context. See also the reference for the this
keyword.
Notice that you should not really use this code. As said, it creates a new instance by invoking the constructor function. But the purpose is only to create an empty object that inherits from the Widget
s prototype object, not to instantiate something (which could do some harm, depending on the code). Instead, you should use Object.create
(or its popular shim):
WeatherWidget.prototype = Object.create(Widget.prototype);
see also Javascript basic inheritance vs Crockford prototypical inheritance
Solution 4:
In plain english you're extending one class with another. A prototype can only be an object so you set WeatherWidget
's prototype to a new instance of Widget
. If you removed the new
keyword you would be setting the prototype to the literal constructor function which doesn't do anything.
var Appendages = function(){
this.legs = 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = new Appendages;
var sara = new Features();
sara.legs;
// Returns 2.
Understanding that the prototype can be any object, something like this would also work:
var appendages = {
legs : 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = appendages;
var sara = new Features();
sara.legs;
// Returns 2.
In JavaScript, if the key isn't found on the object, it checks the parents object you extended it from. Hence you can change items on the parent object on the fly like so:
var appendages = {
legs : 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = appendages;
var sara = new Features();
sara.legs;
// Returns 2.
appendages.hair = true;
sara.hair;
// Returns true.
Note that this all happens during instantiation which means you can't just switch out the prototype after you've created the object:
var foo = {name : 'bob'};
var bar = {nachos : 'cheese'};
foo.prototype = bar;
foo.nachos;
// undefined
However, all modern browsers come with this newer __proto__
method, which allows you to do it:
var foo = {name : 'bob'};
var bar = {nachos : 'cheese'};
foo.__proto__ = bar;
foo.nachos
// "cheese"
Read up more on understanding JavaScript prototypes here. This article from Pivotal Labs is also really good.