ES6 Array.prototype.fill
So recently I tried to initialize an array of empty objects in javascript. I wanted it to be configurable with how many elements are present in the initial array as well as what I initialize it with (it may not be an empty object in the future).
ES6 comes into mind with it’s new Array methods. Using Array.prototype.fill
together with dynamically creating an Array using its constructor
method solves all my problems since its configurable with its initial length and what to fill it with.
Array.prototype.fill() #
The fill() method fills all the elements of an array from a start index to an end index with a static value.
Syntax:
arr.fill(value[, start = 0[, end = this.length]])
Example:
const numberOfBooks = 7;
const myFavouriteBook = 'Harry Potter';
const favouriteBooks = new Array(numberOfBooks);
favouriteBooks.fill(myFavouriteBook);
console.log(favouritebooks);
// ["Harry Potter", "Harry Potter", "Harry Potter", "Harry Potter", "Harry Potter", "Harry Potter", "Harry Potter"]
MDM link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
LGTM right? Sure, if you’re dealing with anything other than objects. Upon further investigation, it seems like whenever I interact with any objects initialized by Array.prototype.fill()
, it gets copied to every other object in that array.
Example:
const objects = new Array(3).fill({});
objects[1].name = 'Harry Potter';
console.log(objects);
// [{"name":"Harry Potter"},{"name":"Harry Potter"},{"name":"Harry Potter"}]
huh?
Well going back to javascript 101, objects are passed by reference. So passing an object into the function fill()
is used by reference. Unfortunately fill()
does not handle it properly (imo) so instead of having an array of individual objects, you end up with an array of indexes all referencing the same object.
Why?
Well even the MDN Polyfill code sample does not handle passing in an object as pass by value. Neither does the spec specify in its steps for browsers to do this specially for arguments that are objects.
MDN Polyfill Code Snippet:
Array.prototype.fill = function(value) {
// ... Steps 1 - 11 (value is not changed)
// Step 12.
while (k < final) {
O[k] = value;
k++;
}
// Step 13.
return O;
};
Solution #
We have to initialize each element of the array with a new Object()
. This way, each new Object is truly separate from the rest and can act independently from the array’s other Objects.
ES6:
const numHarrys = 7;
const harrys = new Array(numHarrys).fill(undefined).map(() => new Object());
harrys[3] = {real: true};
expect(harrys[0].real).to.not.equal(harrys[3].real);
// true
or in ES5:
var numHarrys = 7;
var harrys = Array.apply(null, Array(numHarrys)).map(() => new Object())
harrys[3] = {real: true};
expect(harrys[0].real).to.not.equal(harrys[3].real);
// true
or you know, bring it up to the TC39 as a new proposal?
You heard it here first ;)
P.S. The fill()
method does not work for array-like objects such as arguments or non-es6 compliant browsers.