I have written why I like function hoisting in JS, but the other day I got a bite in the back in the context of function hoisting. 😸
The context was something like this:
export var productCollection = new ProductCollection(jsonData.concat());
And I needed to add a transformation to this collection, so I thought I’d have
a massage(json)
function, so that it’d end up like this:
export var productCollection = new ProductCollection(massage(jsonData));
And, of course I would like to make use of function hoisting and have my function defined somewhere at the bottom, so that I keep the first things coming first. Good, let’s do it: I need to have each product’s category names in a specific order. So:
export var productCollection = new ProductCollection(massage(jsonData));
function massage(json) {
var CATEGORY_ORDER = [
"Red",
"Orange",
"Yello",
"White",
];
return json.map(function(p) {
return p.categoryNames.sort(function(c1, c1) {
return CATEGORY_ORDER.indexOf(c1) - CATEGORY_ORDER.indexOf(c2)
);
);
}
Um… I don’t need to create that CATEGORY_ORDER
array on every function call,
let’s pull it out of the function:
export var productCollection = new ProductCollection(massage(jsonData));
var CATEGORY_ORDER = [
"Red",
"Orange",
"Yello",
"White",
];
function massage(json) {
return json.map(function(p) {
return p.categoryNames.concat().sort(function(c1, c1) {
return CATEGORY_ORDER.indexOf(c1) - CATEGORY_ORDER.indexOf(c2)
);
);
}
Looks good, let’s see if it works. And…
Cannot read property 'indexOf' of undefined!?
Huh… A few minutes went by while staring at the code…
Ooh!! I see: the function definition is hoisted, so when I call it before its use, it works fine. The tricky part is the variable hoisting: only variable declaration is hoisted — its assignment is left where it is, so, at the runtime the code looks more like this:
var CATEGORY_ORDER;
function massage(json) {
return json.map(function(p) {
return p.categoryNames.concat().sort(function(c1, c1) {
return CATEGORY_ORDER.indexOf(c1) - CATEGORY_ORDER.indexOf(c2)
);
);
}
export var productCollection = new ProductCollection(massage(jsonData));
CATEGORY_ORDER = [
"Red",
"Orange",
"Yello",
"White",
];
So at the time when the function is called, the CATEGORY_ORDER
is only
declared, but not yet assigned, which means it’s value is undefined
, and so
the error message now makes perfect sense:
Cannot read property 'indexOf' of undefined
OK. 🤔