The case of global-scoped variables in JavaScript
JavaScript is a language that allows you to declare variables in different ways. You can use the var
keyword, which creates a variable that is function-scoped. This means that the variable is accessible within the function where it is declared, and any nested functions. For example:
function foo() {
var x = 1; // this is a function-scoped variable
function bar() {
console.log(x); // this will print 1, because x is accessible here
}
bar();
}
foo();
You can also use the let
or const
keywords, which create variables that are block-scoped. This means that the variable is accessible within the block where it is declared, and any nested blocks. A block is a set of statements enclosed by curly braces, such as an if
statement, a for
loop, or a try
block. For example:
function foo() {
if (true) {
let x = 1; // this is a block-scoped variable
console.log(x); // this will print 1, because x is accessible here
}
console.log(x); // this will throw an error, because x is not accessible here
}
foo();
However, there is a third way of declaring variables in JavaScript, which is not using any keyword at all. This creates a variable that is global-scoped. This means that the variable is accessible everywhere in the code, even outside of the function where it is declared. For example:
function foo() {
x = 1; // this is a global-scoped variable
}
foo();
console.log(x); // this will print 1, because x is accessible here
This may seem convenient, but it can also lead to some unexpected behavior and errors. One such example is the undefined variable puzzler, which involves a global variable that is declared without a keyword, but is used before it is assigned a value. Consider the following code:
function foo() {
console.log(x); // what will this print?
x = 1;
}
foo();
What do you think will happen when you run this code? Will it print 1
, or will it throw an error?
The answer is… neither! Instead, it will print undefined
! This is because of a feature of JavaScript called hoisting. Hoisting is the process of moving variable declarations to the top of their scope, before the code execution. This means that the code above is equivalent to:
function foo() {
var x; // this is hoisted to the top of the function scope
console.log(x); // this will print undefined, because x has not been assigned a value yet
x = 1; // this will assign a value to x
}
foo();
However, hoisting only applies to the declarations, not the assignments. This means that if you declare a variable without a keyword, it will be hoisted to the top of the global scope, not the function scope. This means that the code above is actually equivalent to:
var x; // this is hoisted to the top of the global scope
function foo() {
console.log(x); // this will print undefined, because x has not been assigned a value yet
x = 1; // this will assign a value to x
}
foo();
This explains why the code prints undefined
, instead of throwing an error. The variable x
is declared in the global scope, but it is not assigned a value until the function foo
is called. Therefore, when the function tries to print the value of x
, it prints undefined
, which is the default value of an uninitialized variable.
The solution to this puzzler is simple: always use a keyword to declare your variables, preferably let
or const
, which are block-scoped and prevent accidental reassignment or redeclaration. This way, you can avoid this puzzler and create variables that are clear and consistent.
JavaScript may be a language full of surprises, but with some attention to detail, you can avoid some of the most common pitfalls and write code that is robust and reliable.