41

I am very confused about how constructors work in Javascrpt; despite using the language for several years (mostly as if it were like a semi-imperative version of LISP) I would like to know more about how objects are supposed to work in it.

Given this code:

function Foo(x) {
    return {
        bar: function() { return x; }
    };
}

What is the difference between calling myFoo = Foo(5) and myFoo = new Foo(5)? Or, in other words, what exactly does a constructor in Javascript do?

fluffy
  • 4,460
  • 28
  • 58
  • 16
    And if they didn't understand those google search results, and came here hoping for a better explanation, what good does your snarky comment do them? – Chris Sobolewski Feb 27 '12 at 16:05
  • @ChrisSobolewski because if we don't know what the OP doesn't understand about it, how can we help him? how can we clarify things if we don't know what's unclear? – Jeff Feb 27 '12 at 16:08
  • 1
    @ChrisSobolewski, some people forget that the purpose of this site is to clear things up that you don't understand. Just because accurate info is available and you understand it. This site works perfectly for me to clear things up. – Evik James Feb 27 '12 at 16:08
  • Alright either I missed the word "in" or it was edited in and makes the comment sound a lot less snarky and actually helpful. I retract my previous statement. :) – Chris Sobolewski Feb 27 '12 at 16:12
  • 2
    related: [What is the 'new' keyword in JavaScript?](http://stackoverflow.com/q/1646698/218196), [What does the new keyword do under the hood?](http://stackoverflow.com/q/3650892/218196). – Felix Kling Feb 27 '12 at 16:18
  • @Oded All of the Google search results were just on things about how `Number` differs from `new Number` and the like (and those were explicitly stated as being special cases in Javascript). I've been working in Javascript (at a very basic level) for years but have never actually figured out how objects are "supposed" to work in it, and obviously I'd like my understanding to improve. – fluffy Feb 27 '12 at 16:57
  • @FelixKling Thanks. I had of course searched StackExchange before posting, but somehow neither of those question pages came up. It doesn't help that the `new` keyword appears pretty much EVERYWHERE... – fluffy Feb 27 '12 at 16:58
  • Even now in 2018, the results (mostly blog articles) I found in google search for "how does new work in javascript" and "how does prototype work in javascript" were unclear (to me), including the MDN tutorial linked by slashnick. The first post I found that I thought was a clear explanation was: https://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/. (I'm not sure whether that explanation is as complete as Mike Samuel's answer below, but at least it was clear and made sense to me.) – M Katz May 22 '18 at 02:15

3 Answers3

50

What is the difference between calling myFoo = Foo(5) and myFoo = new Foo(5)?

There's no difference for that code, because it returns an object, and the spec says:

  • Let result be the result of calling the [[Call]] internal property of F, providing obj as the this value and providing the argument list passed into [[Construct]] as args.
  • If Type(result) is Object then return result.

Since that function returns a result that is an Object, its result is used. You would notice a difference if it did not return an object, or if it checked this, for example if you rewrote it as:

function Foo(x) {
  if (!(this instanceof Foo)) { return new Foo(x); }
  this.bar = function() { return x; };
}
// Now instanceof works.
alert((new Foo) instanceof Foo);

What does new in JavaScript do, anyway?

The new operator causes the function to be called with this bound to a newly created Object whose prototype is that function's prototype property.

For user-defined functions,

new f(a, b, c)

is equivalent to

// Create a new instance using f's prototype.
var newInstance = Object.create(f.prototype), result;

// Call the function
result = f.call(newInstance, a, b, c),

// If the result is a non-null object, use it, otherwise use the new instance.
result && typeof result === 'object' ? result : newInstance

Note, that the language specification actually defines functions with two operations, [[Call]] and [[Construct]], so there are some corner cases where new behaves oddly.

For example, bound and built-in functions:

var g = f.call.bind(f);

should define a function that when called, just calls f, so g should be the same as f in all respects, but

new g()

produces

TypeError: function call() { [native code] } is not a constructor

because the builtin function Function.prototype.call supports [[Call]] but not [[Construct]].

Function.prototype.bind also behaves differently around new and regular calls. The this value is always the bound thisValue when called, but is a newly constructed instance when you use new.

Mike Samuel
  • 109,453
  • 27
  • 204
  • 234
  • 2
    This is informative, but doesn't exactly answer the question. – benekastah Feb 27 '12 at 16:10
  • 3
    @benekastah, There are two questions. One in the title, and one in the text. I answered the title first, and have now edited to answer the one in the text. – Mike Samuel Feb 27 '12 at 16:14
  • 1
    So you did. I withdraw my objection :) – benekastah Feb 27 '12 at 16:53
  • 2
    Thanks, this is finally a straightforward explanation of how Javascript objects work that I haven't been able to find anywhere else (including from our lead Javascript guy who just says it's "obvious" and "prototype-based," as if that answers everything). +1 and accepted. – fluffy Feb 27 '12 at 16:54
  • 1
    @fluffy, You're welcome. When you have questions about core language features like this, the language spec is the best place to go. It's not easy reading but it is authoritative and often has answers that no other document does. – Mike Samuel Feb 27 '12 at 16:59
  • Well, I did try reading it, of course, but I didn't find it particularly clear. :) Having a nice distilled "this is what it means in plain English" is nice, and was also my goal here - I'm sure that if I had trouble with this, others were even worse off. – fluffy Feb 27 '12 at 17:04
  • regarding line `result = f.call(newInstance, a, b, c), result && typeof result === 'object' ? result : newInstance` as it is not using comma-operator the rightmost expression is evaluated, but not returned (returns leftmost expression). Are you certain it shouldn't be `result = (f.call(newInstance, a, b, c), result && typeof result === 'object' ? result : newInstance)` instead (parantheses turns it into comma operator; evals left-to-right, returns rightmost expression)? ```var a, b; a = 0, 1; b = (0, 1); console.log(a, b); // prints "0 1"``` – felix Oct 26 '19 at 10:08
  • @felix, wouldn't result be undefined when typeof applies then? – Mike Samuel Oct 26 '19 at 13:38
  • @MikeSamuel typeof would never apply, as result is declared at first line but not assigned until last. To use an example more similar to your snippet (note that I changed result={} and last line to "aaa : bbb" instead): ```function f() {return true}; var newInstance = Object.create(f.prototype), result = {}; result = f.call(newInstance), result && typeof result === 'object' ? "aaa" : "bbb"; console.log(typeof result, result);``` outputs `boolean true`. So no, when typeof applies, result is assigned return-value of f. While with the parantheses I suggested you get `string aaa` instead. – felix Oct 26 '19 at 14:50
13

In this particular example, there's no difference in the end result.

This is because your Foo function is returning an object instance.

The new operator returns a newly created object that inherits from the constructor's prototype only when the function returns a primitive value (or it doesn't return anything, which is technically the undefined value).

For example:

function Foo () {
  return 5; // or "", or null, or no return statement at all (undefined)
}

var foo = new Foo();
typeof foo; // "object"
foo instanceof Foo; // true
Foo.prototype.isPrototypeOf(foo); // true

When you return an object, the newly created object that inherits from the constructor's prototype is simply discarded:

function Foo () {
  return {};
}

var foo = new Foo();
typeof foo; // "object"
foo instanceof Foo; // false
Foo.prototype.isPrototypeOf(foo); // false

See also:

Community
  • 1
  • 1
Christian C. Salvadó
  • 723,813
  • 173
  • 899
  • 828
6

In this instance there will be no difference as you are returning a new object. It could be rewritten as:

function Foo(x){
   this._x = x;
}

Foo.prototype.bar = function() { 
   return this._x;
}

With this syntax each time you call new Foo it will create a new object with a property of _x. The benefit is that the bar function will be stored once and reused for multiple instances of Foo. With the code in the question calling Foo() multiple times will create a bar function for every instance. So attaching functions to the prototype rather than having them directly on the object will be lighter in memory.

A full break down of how the prototype works can be found at MDC.

slashnick
  • 24,741
  • 10
  • 52
  • 66