4

I've been reading 'Effective JavaScript' lately and I came across this question.

The author explains how it's important to make your Constructor Function new-agnostic because if a developer forgets to call the Constructor with the 'new' keyword, 'this' refers to 'Window'. That makes sense. What's confusing me is the purpose of his implementation.

He advices to set up your constructor like this.

var Person = function(name, age){
  var that = this instanceof Person ? this : Object.create(Person.prototype);
  that.name = name;
  that.age = age;
  return that;
}

That makes sense. You check if 'this' is an instance of Person, meaning it was called with the 'new' keyword. If it's not, create a new Object that does the same thing as 'this' and return that object.

My question is this. If you're setting up a new Object that does the same thing as 'this', can't we just never worry about if the constructor was called with new by foregoing 'this' and just creating the new object.

var Person = function(name, age){
  var that = Object.create(Person.prototype);
  that.name = name;
  that.age = age;
  return that;
}

Why worry about 'this' and 'new' at all and why not always just create our constructors like the one above?

Tyler McGinnis
  • 34,836
  • 16
  • 72
  • 77
  • Yes, that's one approach, though technically when called with `new`, two objects will be created though you're only using one. – cookie monster Jun 03 '14 at 16:08
  • @cookiemonster Wouldn't the extra object be garbage collected though, as there would be no references to it? – Etheryte Jun 03 '14 at 16:10
  • @cookiemonster Which two objects? I've only ever heard of one object being created like above. – Tyler McGinnis Jun 03 '14 at 16:10
  • @Nit: Yes, it's garbage collected. It's just unnecessary overhead. – cookie monster Jun 03 '14 at 16:10
  • 2
    @TylerMcGinnis: One by virtue of the fact that it was invoked using `new`, and the other by the call to `Object.create()`. You're just ignoring the one created from `new`. – cookie monster Jun 03 '14 at 16:11
  • @cookiemonster I think you could just wrap this into an acceptable answer, pun intended. – Etheryte Jun 03 '14 at 16:11
  • 2
    @Nit: I don't feel like retyping it. If you could do it but make it a bit more thorough, I'd give you +1. – cookie monster Jun 03 '14 at 16:13
  • as coded, you break the inheritance of future changes to Person.prototype by coding it the 2nd way... – dandavis Jun 03 '14 at 16:16
  • 3
    @dandavis: No, it still inherits from `Person.prototype`. It's the same construct just using different syntax. – cookie monster Jun 03 '14 at 16:17
  • i was thrown because it's a bit odd to close "Person" from inside the constructor like that, using an anon function prototype as a state object instead of just using an object literal at that point... Person can be re-defined outside the constructor, and that would break inheritance, but it wouldn't happen if the constructor was named like is typical when we use "new Constructor". (We are trying to make it more robust right?) Give it a name and i'm happy. – dandavis Jun 03 '14 at 16:28
  • 1
    @dandavis: If `Person` is redefined, that wouldn't break inheritance. Naturally updates to the new `Person.prototype` wouldn't be reflected in objects that were already constructed, but giving the function a name wouldn't change that fact, unless you're talking about adding properties to `Person.prototype` *inside* the constructor, which would be very odd. – cookie monster Jun 03 '14 at 16:43
  • ...maybe you meant if `Person` is redefined and the original function is retained by a different variable. Then a named function expression could be useful. – cookie monster Jun 03 '14 at 16:59
  • Related: [When should I automatically create an object even if `new` is forgotten?](http://stackoverflow.com/q/20859985/1048572) – Bergi Sep 29 '14 at 00:08

2 Answers2

6

Why worry about 'this' and 'new' at all and why not always just create our constructors like the one above?

Because it is just more concise to write only

function Person(name, age) {
    this.name = name;
    this.age = age;
}

new was invented before Object.create (which is not available in older browsers) and became the standard pattern. Most people are so accustomed to it that they don't bother to include a if (!(this instanceof Person)) return new Person(name, age) check.

If you're setting up a new Object that does the same thing as 'this', can't we just never worry about if the constructor was called with new by foregoing 'this' and just creating the new object.

No, you don't always know how to create the new object. this instanceof Person is also true for anything else that does inherit from Person.prototype, and does allow for class inheritance:

function Employee(name, age, salary) {
    Person.call(this, name, age);
    this.salary = salary;
}
Employee.prototype = Object.create(Person.prototype);

The Person.call(this) wouldn't be possible if you chose to always return a new object.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • *"The `Person.call(this)` wouldn't be possible if you chose to always return a new object."* Why would that make a difference? The return value is ignored, and the `this instanceof Person` succeeds so `that === this`. I'm not understanding what you're getting at here. – cookie monster Jun 03 '14 at 16:53
  • @cookiemonster: It works for his first constructor with the ternary operator, but not for `var that = Object.create(Person.prototype);` in the second constructor – Bergi Jun 03 '14 at 16:55
-1

Yes, the effect is the same, however, it allocates one more object. The constructor is meant to be used with new, and this technique takes car of the cases where the programmer has forgotten the new.

It would be better to throw an exception in the latter case if you ask me.

Maurice Perry
  • 32,610
  • 9
  • 70
  • 97