40

What is the usage of the construct: function F() { if (!(this instanceof F)) { return new F() }; ... }?

I found this in a pty.js for Node. Here is the original code:

function Terminal(file, args, opt) {
  if (!(this instanceof Terminal)) {
     return new Terminal(file, args, opt);
  }

  var self = this
     , env
     , cwd
     , name
     , cols
     , rows
     , term;
-------------------SKIP-----------------------------------
  Terminal.total++;
  this.socket.on('close', function() {
     Terminal.total--;
     self._close();
     self.emit('exit', null);
  });

  env = null;
}
exebook
  • 32,014
  • 33
  • 141
  • 226
  • 1
    This effectively forces the function to be called like `new Terminal(...)`...because if it's not (`if (!(this instanceof Terminal))`), then it forces it (`return new Terminal(file, args, opt);`) – Ian Mar 05 '14 at 23:38
  • 2
    possible duplicate of [When should I automatically create an object even if \`new\` is forgotten?](http://stackoverflow.com/questions/20859985/when-should-i-automatically-create-an-object-even-if-new-is-forgotten) – Travis J Mar 05 '14 at 23:55
  • Related, but not a duplicate. – Shog9 Dec 07 '14 at 03:55

3 Answers3

47

It means that if the function was called without the new operator, it will automagically return a new instance.

For example, if you didn't have this safeguard, and did this...

var t = Terminal();

...then the this while executing Terminal() would point to window (or your global object, fancy non-browser guy/gal), definitely not what you want.

By determining that this is in fact an instance of Terminal, then we can proceed. Otherwise, the safeguard returns a new object.

Then we can simply use both forms...

var t = Terminal(); // Will be same as `new Terminal()`
alex
  • 479,566
  • 201
  • 878
  • 984
  • 5
    +1 irrespective of your inclusion of the word "automagically". ;-D – cookie monster Mar 05 '14 at 23:41
  • 1
    Is the use of this construct considered "bad programming", or would this safeguard + allowing this usage be fine? – Brendan Sep 27 '14 at 23:12
  • 1
    @BrendanAshworth I wouldn't say so. jQuery does the same thing with `$.Deferred()`, and point out in the docs that it's okay to do so, for whatever that's worth. – alex Sep 29 '14 at 00:03
12

It's just to make sure it will work even if F is called without new.

When you call F with new, in that function this is the new instance.

Then, if this is not an instance of F (!(this instanceof F)), then that means that F was not called using new. In this case, F calls itself, now with new.

Oriol
  • 274,082
  • 63
  • 437
  • 513
9

Further to the great explanations in this thread, it is interesting to see what happens under the hood. The ECMAScript specification (where Javascript is based) defines a global object. This is implemented differently in different execution environments. In your typical browser, it is the window object while in Node.js it is the root object. Every function defined "in the wild" (not attached to a user created object) will become a property of the global object. In Node.js you can try:

> function Test() {};
> root.Test
[Function: Test]

Now, the this variable points to the object that the function is a member of. So in the example above:

> function Test() { 
... console.log(this === root); 
... };
> Test()
true

Same goes for your function Terminal. If you run it, this will point to the global object which is of course not an instance of Terminal!

When calling a function using the new operator, an object is returned which will have access to a property called constructor that will point back to that function. It is something equivalent to:

> var instance = {};
> instance.constructor = Terminal;
> instance.constructor();

So, when the conditional fails and the Terminal function is run through the new Terminal() line, this will point to a newly created instance which is of type Terminal!

If you want to get more technical, instance does not have a constructor property itself. It is instead linked (via the prototype chain*), to a private object** (created by the runtime) that has a constructor property pointing to the Terminal function. That private object is pointed back to by the function through a prototype property. D.Crockford presents this in pseudocode as follows:

Terminal.prototype = {constructor: Terminal};

Again, this is only when you call the function with new.

* If a property is not found, the object will look it up at the object pointed to by the __proto__ property.

** (imagine something like an object called _Terminal that you cannot access by name)

Mike M
  • 4,879
  • 5
  • 38
  • 58