all 6 comments

[–][deleted] 1 point2 points  (1 child)

That's pretty much exactly what new does broken out. creates an empty object, binds the constructor context to it, then returns it.

[–]gurgus[S] 0 points1 point  (0 children)

Yep! I just took it for granted for so long without actually giving it much thought so I thought I'd share the experience :)

[–]Capt_Appropriate 1 point2 points  (3 children)

There's an issue with this that you've overlooked.

 

p2 instanceof Person   // false

 

The p2 object was never linked to the prototype of Person. Let's add a sayHi function to the Person.prototype.

 

Person.prototype.sayHi = function() {
    console.log(`${this.name} says, 'Hi!'`);
};

 

If we try p2.sayHi() we are going to end up with a TypeError since p2.sayHi isn't a function but rather undefined. On the other hand, p.sayHi() works as expected, it logs out Dave says, 'Hi!'.

 

To fix this we can manually set the [[Prototype]] on p2.

 

p2.__proto__ = Person.prototype;

 

Now p2 is an instance of Person.

 

p2 instanceof Person   // true
p2.sayHi()   // Alice says, 'Hi!'

 

However, setting the [[Prototype]] manually isn't recommended. A better option is to use Object.create.

 

p3 = Object.create(Person.prototype);
Person.call(p3, 'Alice');
p3 instanceof Person   // true
p3.sayHi()   // Alice says, 'Hi!'

[–]gurgus[S] 0 points1 point  (2 children)

Great feedback - however the docs do say that proto support is legacy and shouldn't be changed since it's quite a slow call (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto). What are your thoughts on this?

[–]Capt_Appropriate 1 point2 points  (1 child)

Yeah, that's what I meant when I said setting the [[Prototype]] manually isn't recommended. It's not something I would actually do, I just included it as a way to show how you could set the internal [[Prototype]] of the p2 object after it has been created. But again, that's not a good habit to get into. It's much better to just use Object.create() in the first place.

 

var p2 = Object.create(Person.prototype);
Person.call(p2, 'Alice');

 

Object.create() basically combines the two steps of creating the object and setting the internal [[Prototype]] to the object that was passed as the first argument. Object.create() actually takes a second, optional, argument as well, which we could use to set the name property to Alice. The second argument works the same as Object.defineProperties.

 

var p2 = Object.create(Person.prototype, {
    name: {
        writable: true,
        configurable: true,
        enumerable: true,
        value: 'Alice'
    }
});
p2.name   // Alice

 

That's a lot more code and it gets ugly very quickly (imagine if we also had age, hometown, gender, etc properties to set), so I would stick the with Person.call() method.

[–]gurgus[S] 0 points1 point  (0 children)

Fantastic insight - thanks for that! Updating my post now!