51

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?

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Kai
  • 2,145
  • 1
  • 20
  • 35

6 Answers6

51
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.

PointedEars
  • 14,752
  • 4
  • 34
  • 33
  • 4
    +1 for despising the bad approach, -1 for not mentioning `Object.create` :-) – Bergi Sep 26 '12 at 01:30
  • +1, fixed. I will see if `Object.create()` is worth using in a branch in `Function.prototype.extend()`. – PointedEars Sep 26 '12 at 01:43
  • This was very interesting to read and learn. In your example using Dummy you wrote `WeatherWidget.prototype = new Dummy();`, in my line the brackets are missing. What difference does that make? – Kai Sep 26 '12 at 02:44
  • @Kai Thank you. The difference is syntactical, not semantical. Consider it good code style. Noticed how the program changes – in that the constructor is not called anymore – when you are omitting `new` from your version? With an argument list the call of the constructor as a function might still fail, but at least it does not fail silently. – PointedEars Sep 26 '12 at 08:58
  • That is, if the constructor tested `this` or used strict mode. – PointedEars Sep 28 '12 at 09:26
  • @PointedEars, using `Object.defineProperty(WeatherWidget.prototype, "constructor", {value:WeatherWidget})` should solve the enumerate problem. – Mouser Jan 20 '15 at 22:36
  • @Mouser Of course, but that requires an implementation of ES 5.x or newer, and then you can use `Object.create(…)` in the first place. See further below :) – PointedEars Jan 21 '15 at 13:16
  • @PointedEars Is it safe to commit to `Object.create()` now (it hasn't been 10 years since your post yet)? It seems all browsers support it. – wcochran Jun 01 '15 at 21:21
  • @wcochran As always, it depends on your target environments (“all browsers” really means “all *browser* versions *I [you] have tested with*”). See the [ECMAScript Support Matrix](http://PointedEars.de/scripts/test/es-matrix/?filter=Object\.create#features-table) for the test cases and results. The feature is not going to go away, so it is safe with anything *recent* (for example, all environments that use Google V8 JavaScript as ECMAScript implementation). I recommend that you use `Object.create()` and emulate it where it is not available (shim). But not all of its features can be emulated. – PointedEars Jun 02 '15 at 14:29
  • Thanks. Btw, you might want to update that statement about "restricted environments". `Object.create` can and should be used on the Web now (and if supporting old IE is a must, then it can be shimmed using that `Dummy` trick). – Bergi Aug 06 '15 at 06:38
  • @Bergi It is not that simple. It does not suffice to determine that the method is there (existence), but you also need to make sure that it works properly if it is there (functionality). And keep in mind that you cannot emulate non-`[[Enumerable]]`. With that said, of the major implementations I/we have tested, so far only [that in the second-newest Opera version did not pass all of the tests](http://PointedEars.de/es-matrix/?filter=Object.create). They do not include the non-`[[Enumerable]]` test yet, though. – PointedEars Aug 06 '15 at 10:10
  • I don't think non-enumerabilty is a deal breaker here. Btw, that test case seems to fail in Opera because `eval` in a `catch` statement does not work as specced, but the test uses it - try out `var e = "variable"; try { throw "lexical"; } catch(e) { console.log(eval("e")); }`. `Object.create` behaves as expected actually. (Seems I wrote you an email about this some 2 years ago already?) – Bergi Aug 06 '15 at 16:23
  • @Bergi *If* you emulate `Object.create()`, you need to emulate it *properly* because then it might not only be used for inheritance; it has a second parameter. Non-`[[Writable]]` *can* be emulated in many known, non-`[[Enumerable]]` and non-`[[Configurable]]` *might* be emulated in some environments. As for the test case, I have received your e-mail, kept it, and not replied to it because I have not found the time to determine the reliability of your test case, and a compatible workaround for the problem, if it is that, yet. – PointedEars Aug 08 '15 at 08:03
  • @PointedEars: I think `if (errorHandlers == "e") return e;` would help the majority of cases; if you want to make sure just deprecate `evil` code strings and always pass a function (the identify function instead of `"e"`). – Bergi Aug 08 '15 at 16:13
  • @Bergi Your suggestion cannot work, and I cannot replace `eval(…)` in the ECMAScript _Support_ Matrix; that would defy its purpose. (This discussion does not belong here, and discussions in general do not belong on Stack Overflow. Please, if necessary, continue it elsewhere. Previously, a link to a chat was offered when there were too many comments on an answer, but I do not see that anymore.) – PointedEars Aug 08 '15 at 16:23
  • In your example `WeatherWidget.prototype = new Dummy();` , `new Dummy()` will return an object whose prototype is `Widget.prototype`. If it was `new Widget()`, it'll also return an object whose prototype is `Widget.prototype`. What's the difference..? Am I missing something..? – T J Nov 18 '15 at 20:22
  • @TJ Your observation is correct. The difference is: With the "bad" approach, “your `WeatherWidget` instances will all inherit from the *same* `Widget` instance” *before* they inherit from `Widget.prototype`. This makes them dependent on the `Widget` constructor, and to share the values of all properties that are provided by `Widget.prototype` and defined in the `Widget` constructor, through `WeatherWidget.prototype`. – PointedEars Nov 21 '15 at 17:17
  • `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.` Why is this a problem? It sounds exactly like how inheritance should work. One object has an effect on downstream properties. – Melab Aug 10 '21 at 13:51
7

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:

  1. There is a brand new object created
  2. Widget can use the this keyword to refer to that object.
  3. If Widget does not return anything, this new object will be created.
  4. 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

  1. If in strict mode, this will be set to undefined.
  2. Otherwise, this will refer to the global object. (Called window by the browser.)
  3. If the function does not return anything, then undefined will be returned.

Reference: new keyword

Jeremy J Starcher
  • 23,369
  • 6
  • 54
  • 74
  • Searching for `javascript constructor` here at SO will fill in many more details. – Jeremy J Starcher Sep 26 '12 at 00:22
  • Missing obligatory link to https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/new :-) – Bergi Sep 26 '12 at 00:23
  • Please note that his code has no brackets - it would be just an assignment of the constructor function – Bergi Sep 26 '12 at 00:24
  • 2
    @Bergi -- In Javascript constructors the parens are optional for some reason I've never figured out. [See this discussion](http://stackoverflow.com/questions/6439290/parentheses-after-new-function-optional) – Jeremy J Starcher Sep 26 '12 at 00:26
  • 1
    Yes, and that is why it would be no invocation without the `new` keyword - because the OP has omitted them – Bergi Sep 26 '12 at 00:48
  • @JeremyJStarcher You are missing the point. The original code has no parameter list. If you would omit the `new` keyword, the code would not call the constructor at all. So your answer actually does *not* answer correctly the question what would happen if one omitted `new`. – PointedEars Sep 27 '12 at 16:10
  • How does this explain the difference between `a = new Widget()` and `a = Widget()` in way different than saying `new` invokes a function as a constructor? – Melab Aug 10 '21 at 13:55
5
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 Widgets 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

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    Interesting approach, but please see my updated answer: You would want to fix the `constructor` property as well. And that is where the "popular shim" for `Object.create()` fails – *must* fail even if updated – because you cannot emulate non-enumerability. You can only `hasOwnProperty()`-filter it out later. That still leaves write-onlyness and non-configurability, which can be only partially emulated (depending on the implementation). – PointedEars Sep 26 '12 at 20:03
  • Yes, passing `{constructor:{value:WeatherWidget}}` as the second argument should be done, and you might need to use a less popular shim :-) However, I am not sure which code depends on a correctly set `constructor` property? – Bergi Sep 26 '12 at 20:18
  • I have an `isInstance()` method on some of my constructors that does. More precise than duck typing alone. – PointedEars Sep 26 '12 at 20:43
3

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.

Mauvis Ledford
  • 40,827
  • 17
  • 81
  • 86
  • What is that `Appendages` function good for, why do you need an instance (including a prototype object) of it there? – Bergi Sep 26 '12 at 00:47
  • Bergi: I just added a 3rd example to show that off. Objects can extend on each other up a chain so it enables you to reuse and build code. In a theoretical example, all vehicles have similarities including an engine. We can build that into a base class, but not all vehicles have four wheels. So we can build one car class and one motorcycle class on top of it and reuse the core code. In real life, I use the Backbone.js framework. A lot of views are similar where I need to show a list of items. By making a base list class, I can extend it to create 20 different list pages with very little bloat. – Mauvis Ledford Sep 26 '12 at 01:10
  • It's better for `Appendages` to be a class constructor instead of an object so that you can initialize it with various options. So it could be an "engine" class where you can pass in the size of the engine while still making it reusable. – Mauvis Ledford Sep 26 '12 at 01:12
  • Nah, a "class constructor" to instantiate prototype objects is usually a bad idea, the problem of similiar classes should be solved otherwise. – Bergi Sep 26 '12 at 01:28
  • You're right if what you passed to the constructor turned it into a completely different object, but for a minor setting I disagree. – Mauvis Ledford Sep 26 '12 at 01:32
2

new is important for prototype inheritance; i.e.
Create a constructor with a method

var Obj = function(){};
Obj.prototype = {};
Obj.prototype.foo = function(){console.log('foo');};

Make a second constructor to extend the first with

var ExObj = function(){};

Now, if we prototype without new,

ExObj.prototype = Obj;
(new ExObj).foo(); // TypeError: Object #<Object> has no method 'foo'

Which means we haven't inherited from the prototype of Obj, however, if we prototype with new

ExObj.prototype = new Obj();
(new ExObj).foo(); // console logs 'foo'

Furthermore, adding new things to the prototype of ExObj doesn't make any changes to it's base, Obj.

Paul S.
  • 64,864
  • 9
  • 122
  • 138
0

JavaScript functions are "MULTIPLE(2) PERSONALITIES"!!!

They are regular-functions with input and output, which we call like function().

Also they are constructors of JS-objects, when we use the new keyword. >>>BUT<<< the new created objects are NOT INSTANCES of the constructors (like the objects of classes in class-based inheritance). The new objects are instances of the object of the prototype property of the constructor.

Then in WeatherWidget.prototype = you put the object you want to inherit its properties to the objects the constructor will create, which usually is new function() and not a function.

JavaScript created HUGE confusion in the programming community by naming the objects created by constructors, INSTANCES of them with the instanceof keyword.
> function f(){}
undefined
> new f() instanceof f
true

synagonism
  • 135
  • 1
  • 4
  • 1
    Well, you can always do `var o = Object.create(f.prototype); o.constructor(); f.prototype.isPrototypeOf(o)` :-) – Bergi Sep 15 '14 at 18:57