Scope in JavaScript
Let's dive into an important concept in JavaScript: scope. Scope is all about where your variables and functions can be accessed in your code.
Without understanding scope you might have trouble figuring out why some variables are available in one part of your code but not another.
In JavaScript, there are a few types of scope: global scope, local scope, and block scope.
Global Scope
Variables declared outside of any function or block have a global scope and are accessible from anywhere in the code. This means, inside your functions you can reach out and refer to variables that are not available inside your function:
const globalVariable = 'I am global'; function displayGlobalVariable() { console.log(globalVariable); // Outputs: I am global } displayGlobalVariable(); console.log(globalVariable); // Outputs: I am global
In this example, globalVariable
is accessible both inside the displayGlobalVariable
function and outside it.
Local Scope
Variables declared inside a function are in the local scope of that function. They can only be accessed within that function. Any code outside of your function will not be able to access those variables:
function displayLocalVariable() { let localVariable = 'I am local'; console.log(localVariable); // Outputs: I am local } displayLocalVariable(); console.log(localVariable); // Error: localVariable is not defined
In this example, localVariable
is only accessible inside the displayLocalVariable
function. Trying to access it outside the function results in an error.
Block Scope
Variables declared with let
or const
inside a block (a pair of curly braces {}
) are only accessible within that block. Here's an example:
{ let blockScopedVariable = 'I am block scoped'; console.log(blockScopedVariable); // Outputs: I am block scoped } console.log(blockScopedVariable); // Error: blockScopedVariable is not defined
In this example, blockScopedVariable
is only accessible inside the {}
block. This might not feel super useful right now, but later as we learn more concepts it'll be a useful thing to know.
Function Scope vs. Block Scope with var
So far, we have declared all variables with either const
or let
. These are newer features of JavaScript. Before the introduction of let
and const
, JavaScript only had function scope for variables declared with var
. Variables declared with var
are function-scoped and not block-scoped, which means they are accessible throughout the entire function they are declared in, even if they are inside a block like an if
statement or a loop.
function displayVarScope() { { var functionScopedVariable = 'I am function scoped'; } console.log(functionScopedVariable); // Outputs: I am function scoped } displayVarScope();
In this example, functionScopedVariable
is accessible throughout the entire displayVarScope
function, even though it was declared inside an {}
block.
Why Avoid var
?
I've intentionally avoided using var
because I couldn't think of a scenario other than reading older codebases that you might find helpful. The var
keyword has some quirks that can lead to unexpected behavior. Let's look at a few examples to understand why let
and const
are generally preferred.
Function Scope vs. Block Scope
Example with var
:
function displayVarScope() { if (true) { var functionScopedVariable = 'I am function scoped'; } console.log(functionScopedVariable); // Outputs: I am function scoped } displayVarScope();
In this example, functionScopedVariable
is accessible throughout the entire displayVarScope
function, even though it was declared inside an if
block. This is because var
is function-scoped, not block-scoped.
Example with let
:
function displayLetScope() { if (true) { let blockScopedVariable = 'I am block scoped'; } console.log(blockScopedVariable); // Error: blockScopedVariable is not defined } displayLetScope();
In this example, blockScopedVariable
is not accessible outside the if
block where it was declared. This is because let
is block-scoped, making the code more predictable and easier to manage.
Hoisting
Example with var
:
function hoistingExample() { console.log(hoistedVariable); // Outputs: undefined var hoistedVariable = 'I am hoisted'; } hoistingExample();
In this example, hoistedVariable
is hoisted to the top of the function scope. This means the variable declaration is moved to the top of the function, but the initialization stays in place. The result is that the console.log
statement does not throw an error, but it outputs undefined
because the variable is not yet initialized.
Example with let
:
function hoistingWithLetExample() { console.log(notHoistedVariable); // Error: Cannot access 'notHoistedVariable' before initialization let notHoistedVariable = 'I am not hoisted'; } hoistingWithLetExample();
In this example, trying to access notHoistedVariable
before it is declared results in an error. This is because let
does not hoist in the same way var
does, which helps prevent the confusion and bugs associated with hoisting.
By using let
and const
, you get block-scoped variables and avoid the pitfalls of hoisting, making your code cleaner and less prone to errors. Most modern JavaScript developers prefer using let
and const
over var
.
Skills to Practice
Global Scope: Declare a variable in the global scope and access it inside and outside a function.
- Example:
let globalVar = 'Global'; function showGlobalVar() { console.log(globalVar); // Should print 'Global' } showGlobalVar(); console.log(globalVar); // Should also print 'Global'
Local Scope: Declare a variable inside a function and try to access it outside the function.
- Example:
function localScopeExample() { let localVar = 'Local'; console.log(localVar); // Should print 'Local' } localScopeExample(); console.log(localVar); // Should give an error
Block Scope: Use
let
orconst
inside a block and try to access the variable outside the block.- Example:
if (true) { let blockVar = 'Block'; console.log(blockVar); // Should print 'Block' } console.log(blockVar); // Should give an error
Function Scope vs. Block Scope with
var
: Declare a variable withvar
inside a block and see where it is accessible.- Example:
function varScopeExample() { if (true) { var varScoped = 'Function Scope'; } console.log(varScoped); // Should print 'Function Scope' } varScopeExample();
Nested Scope: Create nested functions and access variables from outer and inner scopes.
- Example:
let globalOuter = 'Global Outer'; function outerFunction() { let localOuter = 'Local Outer'; function innerFunction() { let localInner = 'Local Inner'; console.log(globalOuter); // Should print 'Global Outer' console.log(localOuter); // Should print 'Local Outer' console.log(localInner); // Should print 'Local Inner' } innerFunction(); } outerFunction();
Understanding scope is crucial for managing variable accessibility and avoiding conflicts in your code. Practice these concepts to become more proficient in handling scope in JavaScript.