JS: new keyword and functions

Published on: October 12, 2015

Tags: js and interview-questions

I’m still slowly making my through the javascript interview questions, despite having started a new job on JS. There's just so much I don’t know! Maybe one day I'll actually interview someone with these questions...

Alright, let’s get to today’s question:

What’s the difference between: function Person(){}, var person = new Person(), and var person = Person()?

Gah! Too many things!

Let’s take them one by one:

1. function Person(){}

Ok, well this defined a function. Hopefully that was fairly obvious by the function keyword.

Wow, mind blowing stuff here...

The more interesting side of it is the Person. Now, the name of the function is user defined (i.e. not a keyword like function), but what’s interesting here is the capital P. This is a JS convention. (JS doesn’t care at all if you follow it or not, but all the other devs will be sad if you don’t.) The convention is that if a function name starts with a capital letter then that function defines a constructor (similar to a class in other languages). So we can infer that function Person(){} defines a constructor.

Ok, so functions with names starting with a capital letter are usually constructors.

Yep, it’s really, really common (hopefully you’re using this convention too!), so it’s a pretty safe bet. Let’s move on.

2. var person = new Person()

So in many other languages we’d say “The new keyword creates an instance of the Person class.”

But this is JS, not some “other language.”

Yes, very true. JS is a world unto itself. But I hope the sentence above will probably help someone from a more traditional object-oriented background.

Ok, so how does new work for JS?

new does three main things:

  1. new creates a new object. It’s just a plain old, bog standard, nothing-in-it object. It looks like {}. Boring, I know, but it’s very important.
  2. The newly created object has it’s prototype set to whatever the Person’s prototype is right now.
  3. Finally the constructor function is called (the body of Person) with any references to this replaced with the object created in step 1.

Why is this important?

Although I knew the final outcome of using the new keyword before, having those three steps spelled out helped remove some of the magic. So let’s talk about them in more detail.

Step 1 (the plain old empty JS object) means that you get a unique “deep copy” (other languages would say “instance”) of the constructor each time it’s run. If new didn’t create a new object then you’d constantly be overwriting things in seemingly different objects.

Step 2 (setting the prototype) means that you can set methods on the constructor’s prototype and they’ll be available on your new object. Something like this:

1
2
3
4
5
6
7
8
9
function Person(name) {
    this.name = name;
}
Person.prototype.introduce = function() {
    console.log("Hi, my name is " + this.name);
}

var lucy = new Person('Lucy');
lucy.introduce(); // logs out: "Hi, my name is Lucy"

If lucy’s prototype hadn’t been set to Person’s prototype then the introduce method wouldn’t have been available.

Step 3 (constructor with this set) means that each object’s this points to the object, rather than the window or something else. Without the third step this from the Person constructor wouldn’t work correctly and lucy wouldn’t be able to introduce herself.

Huh, ok, so the new keyword is super important for getting new objects. See what I did there?

Moving on...

3. var person = Person()

Things get a little tricky here, since we don’t actually have the Person constructor. It can go one of three ways so let’s look at all of them.

Scenario 1 - everything is ruined

Person is a constructor that was written to work with the new keyword, such as the Person in my example above. In this case things will break, hopefully in an expected way given the three steps above.

So, without the new keyword from above those three steps won’t happen. Let’s look at what that means step by step. We’re going to use the same example as above, but without the new keyword:

1
2
3
4
5
6
7
8
function Person(name) {
    this.name = name;
}
Person.prototype.introduce = function() {
    console.log("Hi, my name is " + this.name);
}

var lucy = Person('Lucy'); // <-- NO NEW KEYWORD

Step 1 (the plain old empty JS object) doesn’t happen. Now instead of getting a new object for lucy all we get is the return value of Person. Which is nothing (a.k.a undefined). Bummer.

Step 2 (setting the prototype) doesn't happen. Well that’s kind of a given. Since there’s no new object (see step 1 that didn’t happen) there can’t be a prototype set on it.

Step 3 (constructor with this set) tries to happen. It really does it’s very best. Since there’s no new object to set this to, JS does the next best thing and uses the default this, the window. So now there’s a brand new property on the window, and you can call it with window.name or this.name both of which are "Lucy".

Well that’s a problem.

Yep, leaving of the new keyword when it’s expected will cause all kinds of headaches.

What’s the “when it’s expected” caveat for?

Great question! The above scenario is, I think, the most likely one, both for what the question was asking about, and in buggy real life code. However, there are two other options. Let’s look at them now.

Scenario 2 - nobody cares

It’s possible that the Person constructor was written to avoid just this pitfall. It would look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Person(name) {
    if (this instanceof Person) {
        this.name = name;
      } else {
        return new Person(name);
      }
}
Person.prototype.introduce = function() {
    console.log("Hi, my name is " + this.name);
}

var lucy = new Person('Lucy'); // <-- NEW KEYWORD IS PRESENT
lucy.introduce(); // logs out: "Hi, my name is Lucy"

var bob = Person('Bob'); // <-- NO NEW KEYWORD
bob.introduce(); // logs out: "Hi, my name is Bob"

This takes advantage of the three steps that new goes through as discussed above. By the time the constructor is actually run there should be an object assigned to this with it’s prototype set correctly so this instanceof Person is true. However if someone happened to forget the new keyword then the constructor would go straight to the else section and create a new object, using the new keyword correctly.

This means using the constructor with and without the new keyword works correctly (as shown by bob and lucy above).

That’s a pretty sneaky trick!

Yeah, it’s quite clever. It’s also really defensive programming. I mean, we can’t write everything to allow important keywords to just be forgotten... I think it’s bad practice (not to mention a little mean) to hide mistakes from people. I want to know when I mess up. Which brings us nicely to:

Scenario 3 - warning

Finally we reach a happy middle ground. Scenario 1 failed quietly. Scenario 2 didn’t fail at all, but also allowed incorrect code to work correctly. But scenario 3 will just give a warning, and it’s quite a simple fix.

A while ago I wrote about strict mode, which is exactly what we’ll use now. Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
"use strict"; // <-- THIS LINE HERE

function Person(name) {
    this.name = name;
}
Person.prototype.introduce = function() {
    console.log("Hi, my name is " + this.name);
}

var lucy = new Person('Lucy'); // <-- NEW KEYWORD IS PRESENT
lucy.introduce(); // logs out: "Hi, my name is Lucy"

var bob = Person('Bob'); // <-- NO NEW KEYWORD
// throws "Uncaught TypeError: Cannot set property 'name' of undefined"

As you can see, using the new keyword works exactly as expected. However, if you forget to use new an error will be thrown, alerting you to your mistake.

What’s happening behind the scenes?

Basically the "use strict" just means that JS won’t allow the this in the constructor to be bound to the window. Instead it leaves this as undefined (since no new object was created to be properly bound to this) which causes the error Cannot set property 'name' of undefined". I think it’s pretty slick.

Note: I couldn’t get the "use strict" to work properly in this window’s console when I ran it just now, so here’s a JSFiddle of the example if you want to double check. Just be sure to have the console of that window open to see the error.

Recap

That was quite a lot, can I have it one more time?

Sure thing, let’s wrap it up!

Our question was "What’s the difference between: function Person(){}, var person = new Person(), and var person = Person()?"

The first one function Person(){} defines a function. Since it’s got a capital letter at the beginning of the function name we expect that it’s a constructor.

Next var person = new Person() is one way to create new objects. Using this method person will have access to everything Person.prototype has access to, as well as any instance variables set in the Person constructor.

Finally var person = Person() is a mistake. There are ways of dealing with mistakes like this (my preference is the "use strict" method), but ultimately this should be corrected.

Done?

Done! That was a long one - thanks for sticking with me!

References


comments powered by Disqus