Hoisting: Variable Declaration Lifting - Images: Japanese
Hey there, everyone!
Next up, let's talk about JavaScript 'hoisting'. This refers to the behavior where declarations are automatically moved to the top of their scope behind the scenes. It's one of the darker corners of JavaScript.
So far on this site, we've covered how to declare variables. Something like this:
function hoge(){
var x;
console.log(x); // Since 'x' is only declared and not assigned, this outputs 'undefined'.
}
hoge();
Here, we define a function called hoge, declare a local variable x inside it, and then log it to the console. Since x is only declared and never assigned a value, the output is undefined. Nothing surprising there.
Now, what happens if we flip the order — putting the console output before the variable declaration?
function hoge(){
console.log(x);
var x;
}
hoge();
Programs normally run from top to bottom, so you'd expect this to throw an error since we're trying to output 'x' before it's been declared. But in JavaScript — it doesn't error. It outputs 'undefined' just fine.
The reason is that JavaScript silently runs a process that says "treat all declarations as if they happened at the top of the scope." Since JavaScript scopes only exist inside functions, this effectively means "treat all declarations as if they appeared at the very beginning of the function." This is the behavior known as 'hoisting'.
※ For more on scope, see this article.
To make things even trickier, only the declaration itself gets hoisted — not the assignment. So assignments still happen in the order you wrote them. Take a look at this sample:
function hoge(){
console.log(x); // Only the declaration is hoisted, so this outputs 'undefined'.
var x = 0; // The assignment happens here.
console.log(x); // After the assignment, this outputs '0'.
}
hoge();
Quite a weird delightful feature, isn't it?
This behavior is rare in other programming languages, which makes it a surprisingly easy source of bugs. Take a look at the sample below. It's an example where someone accidentally introduced a hoisting bug while trying to read from a global variable x at the start of function hoge:
var x = 0; // This is a global variable.
function hoge(){
console.log(x);
// Imagine there's a lot of code here.
// ...
// ...
// ...
// ...
// ...
var x = 1;
}
hoge();
If you've got a good eye for this, you've probably already figured out what happens — the console outputs undefined, not 0.
The reason is that only the declaration is hoisted, which makes the code above behave as if it were written like this:
var x = 0; // This is a global variable.
function hoge(){
var x; // Only the declaration is hoisted. This creates a local variable 'x', not a reference to the global — so it starts as 'undefined'.
console.log(x); // Outputs 'undefined', from the local variable 'x'.
// Imagine there's a lot of code here.
// ...
// ...
// ...
// ...
// ...
x = 1; // Assigns '1' to the local variable 'x'.
}
hoge();
So when you accidentally define a local variable with the same name as a global, the global becomes unreachable inside that function — and JavaScript won't warn you about it at all. If you're not aware of what's going on under the hood, this kind of bug can really bite you.
By the way, this applies not just to plain variables, but to arrays and objects as well:
function hoge(){
console.log(x); // Declaration is hoisted, so this outputs 'undefined'.
console.log(y); // Declaration is hoisted, so this outputs 'undefined'.
var x = [0, 1]; // The assignment happens here, making 'x' an array.
var y = { // The assignment happens here, making 'y' an object.
"hoge": 0,
"hoge1": 1
};
console.log(x); // After the assignment, this outputs '[0, 1]'.
console.log(y); // After the assignment, this outputs '{"hoge": 0, "hoge1": 1}'.
}
hoge();
One common strategy for avoiding hoisting-related bugs is to move all variable declarations to the top of the function. So instead of writing code like this:
function hoge(){
var x = 0;
console.log(x);
var y = 1;
console.log(y);
var z = 2;
console.log(z);
}
hoge();
You'd rewrite it like this:
function hoge(){
var x = 0,
y = 1,
z = 2;
console.log(x);
console.log(y);
console.log(z);
}
hoge();
This pattern is quite common and shows up frequently in textbooks and reference material.
That said, some people prefer code that flows from top to bottom without jumping around.
When all declarations are grouped at the top, reading through the code can mean frequently scrolling back to the top to check what a variable contains, then scrolling back down to where you were — a bit of a back-and-forth. Some developers find it more readable to declare and assign variables right where they're needed. Personally, I'm in that camp too.
If that's your style, one approach is to declare a single object at the top of the function and do all your work inside that object. Like this — it lets you keep a top-to-bottom flow throughout:
function hoge(){
var elem = {}; // Declare the object at the top.
elem.x = 0; // Work only inside the declared object from here on.
console.log(elem.x);
elem.y = 1; // Assign when needed.
console.log(elem.y);
elem.z = 2; // Assign when needed.
console.log(elem.z);
}
hoge();
The key point in all of these approaches is the same: "declare local variables or objects at the top of the function to prevent hoisting-related bugs." So this object-based pattern achieves the same goal as grouping all variable declarations at the top. That said, it's a less common style, so be aware that others reading your code may not be used to it.
That wraps up hoisting. It can feel confusing at first, but stick with it — you'll get there.
In the next article, we'll look at anonymous functions. See you there!
This article was written by Sakurama.
Author's beloved small mammal |
桜舞 春人 Sakurama HarutoA Tokyo-based programmer who has been creating various content since the ISDN era, with a bit of concern about his hair. A true long sleeper who generally feels unwell without at least 10 hours of sleep. His dream is to live a life where he can sleep as much as he wants. Loves games, sports, and music. Please share some hair with him. |
If you find any errors or copyright issues, please contact us.