Caveat: Prototype Pattern 'Constructor' Link Overwrite

During my fix to learn and implement Javascript design patterns, my OCD side noticed that there was a 'variation(?)' to the prototype pattern that I was seeing in some parts of our codebase.

For clarity sake, this is the textbook Prototype Pattern

// Constructor function to create Person objects
function Person(name, age, gender) {  
    this.name = name;
    this.age = age;
    this.gender = gender;
}

// Constructor prototype to share properties and methods
Person.prototype.sayName = function() {  
    console.log('My name is ', this.name);    
};

Person.prototype.sayAge = function() {  
    console.log('My age is ', this.age);
}

In our codebase, I was seeing a nice 'short-hand(?)' to declare methods on the prototype by assigning an object literal with the methods to Person.prototype.

// Constructor function to create Person objects
function Person(name, age, gender) {  
    this.name = name;
    this.age = age;
    this.gender = gender;
}

// Constructor prototype to share properties and methods
Person.prototype = {  
    this.sayName: function() {
        console.log('My name is ', this.name);    
    },

    this.sayAge = function() {
        console.log('My age is ', this.age);
    }
}

But something didn't sit right and I remembered reading about issues using this object literal short-hand.

Caveat: You're Overwriting the Prototype Object!

Say we've created a new jim object, from the Person constructor function.

var jim = new Person('Jim', 12, 'Male');  

The caveat with using the short-hand variation of the prototype pattern occurs when you try to call the jim.constructor property. You would expect jim.constructor to point to the Person constructor function. Instead, it points to the function Object {...} constructor function. What?

The fix is to explicitly declare the constructor property on Person.prototype when using the object literal assignment to the function's prototype.

Person.prototype = {  
    constructor: Person,

    .... //other methods
}
Prototype Chain: What's Happening In the Background

When you define a function, a prototype object (example: Person.prototype for the constructor function) is automatically created with a default property Person.prototype.constructor which points to the constructor function.

All new objects created from Person can access and reference the Person constructor function via the prototype chain, which delegates to Person.prototype.constructor.

1: Person constructor function is created.

var Person = function () {...}  

2: Person.prototype object is automatically created with a default property, constructor.

Person.prototype = {  
    constructor: Person
}

3: A new object jim is instantiated from Person. The object jim is automatically linked to Person.prototype via the prototype chain.

Note: jim.hasOwnProperty('constructor') //false

var jim = new Person('Jim', 12, 'Male');  

4: Calling jim.constructor actually calls Person.prototype.constructor via the prototype chain.

Person.prototype = {  
    constructor: Person
}

5: However, if Person.prototype is overwritten, Person.prototype.hasOwnProperty('constructor') is now false! The call then delegates further up the prototype chain to the base Object constructor function.

This is why we were seeing jim.constructor pointing to the function Object {...} constructor function.

So, using an object literal to define the methods on Person.prototype, actually overwrites the object.

To allow instantiated objects to reference the constructor function, the property Person.prototype.constructor needs to be manually declared.

Duncan Leung

Front End Developer at iHerb.com

Irvine, CA

Subscribe to Duncan Leung: Javascript and Front End Development

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!