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
- Function Constructor:
function EmployeeNames() {}
defines a constructor function for creatingEmployeeNames
objects.
- Prototype Assignment:
- The
EmployeeNames.prototype
is assigned an object with anames
property (an empty array) and ashowNames
method that returnsthis.names
.
- The
- Creating Instances:
var e1 = new EmployeeNames();
creates an instance ofEmployeeNames
, ande1.names.push("foo");
pushes the string “foo” into thenames
array.console.log(e1.showNames());
logs['foo']
.var e2 = new EmployeeNames();
creates a second instance,e2
, ande2.names.push("bar");
pushes the string “bar” into the samenames
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']
aftere1
pushes “foo”.
- The
- Second
console.log(e2.showNames())
:- When
e2
pushes “bar”, it modifies the sharednames
array, resulting in['foo', 'bar']
being logged for bothe1
ande2
.
- When
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.