Comprehending Prototype Behavior in JavaScript

Comprehending Prototype Behavior in JavaScript

I recently came across an intriguing piece of JavaScript code involving prototypes and object instances. This code highlights a common pitfall when working with objects and their prototypes. Let’s take a closer look at what this code does and why it behaves in a particular way.

Code Overview

Here is the code snippet:

function EmployeeNames() {}

EmployeeNames.prototype = {
    names: [],
    showNames: function() {
        return this.names;
    }
};

var e1 = new EmployeeNames();
e1.names.push("foo");
console.log(e1.showNames()); // --> foo

var e2 = new EmployeeNames();
e2.names.push("bar");
console.log(e2.showNames()); // --> foo, bar

Explanation of the Code

  1. Function Constructor:
    • function EmployeeNames() {} defines a constructor function for creating EmployeeNames objects.
  2. Prototype Assignment:
    • The EmployeeNames.prototype is assigned an object with a names property (an empty array) and a showNames method that returns this.names.
  3. Creating Instances:
    • var e1 = new EmployeeNames(); creates an instance of EmployeeNames, and e1.names.push("foo"); pushes the string “foo” into the names array.
    • console.log(e1.showNames()); logs ['foo'].
    • var e2 = new EmployeeNames(); creates a second instance, e2, and e2.names.push("bar"); pushes the string “bar” into the same names array.
    • console.log(e2.showNames()); logs ['foo', 'bar'].

Why Does This Happen?

The reason e1 and e2 share the same names array lies in how prototypes work in JavaScript. When EmployeeNames.prototype is defined with a names array, this array is shared across all instances of EmployeeNames created using the new keyword. This means any modification to names by one instance affects all instances, as they all reference the same array in the prototype.

Breakdown of Log Results

  • First console.log(e1.showNames()):
    • The names array initially contains ['foo'] after e1 pushes “foo”.
  • Second console.log(e2.showNames()):
    • When e2 pushes “bar”, it modifies the shared names array, resulting in ['foo', 'bar'] being logged for both e1 and e2.

Solution to Avoid Shared State

To avoid this issue, it’s better to initialize properties like names inside the constructor function itself:

function EmployeeNames() {
    this.names = [];
}

EmployeeNames.prototype.showNames = function() {
    return this.names;
};

With this change, each instance of EmployeeNames will have its own names array, ensuring that modifications to e1.names do not affect e2.names and vice versa.

Scroll to Top