Declaring Variables

Understanding variables and constants

One of the core concepts in programming is the use of names, or identifiers, to represent values. This practice allows us to easily refer to and manipulate these values throughout our code. When you "bind" a name to a value, you’re assigning that value to a variable, creating a handy label that you can use again and again.

The term "variable" suggests that the value it holds can change. Indeed, you can reassign different values to the same variable as you run your code. However, if you assign a value that should never change, you’ll use something called a constant instead of a variable.

Before you can use a variable or a constant in your code, you need to declare it. In modern JavaScript (since ES6), you do this with the let and const keywords.

  • let is used for variables, which means the value can be reassigned later. It’s good practice to give your variables an initial value right when you declare them. If you don’t, the variable exists, but its value is undefined until you assign something to it.
  • const is used to declare constants, meaning the value is set once and never changes. When you declare a constant with const, you must give it an initial value right away—no exceptions! If you try to change the value later, JavaScript will throw a TypeError, stopping you in your tracks.
// Using let for variables
let score = 10; // Declare a variable with an initial value
console.log(score); // Output: 10

score = 20; // Reassign a new value to the variable
console.log(score); // Output: 20

// Using const for constants
const pi = 3.14159; // Declare a constant with an initial value
console.log(pi); // Output: 3.14159

// Attempting to reassign a value to a constant will throw an error
// Uncommenting the line below will result in a TypeError
// pi = 3.14;          // Error: Assignment to constant variable.

variable scope

The scope of a variable refers to the specific area in your code where that variable is recognized and can be used. In JavaScript, variables and constants declared with let and const are block scoped, meaning they only exist within the block of code (usually within a pair of curly braces {}) where they are declared.

Think of it like this: If you declare a variable or constant inside a set of curly braces, those braces create a "box" around the code where that variable or constant is valid. Once you step outside that box, the variable or constant is no longer accessible.

Interestingly, when you declare variables or constants as part of loops like for, for/in, or for/of, their scope is limited to the body of the loop, even though they might appear outside the curly braces. This can be a little tricky at first but is important to understand for avoiding scope-related bugs.

If you declare a variable or constant at the very top level of your code, outside of any blocks, it's considered a global variable or global constant. Global variables and constants are accessible throughout the entire file they are defined in, no matter where you try to use them in that file. Understanding the scope of your variables and constants is crucial to controlling where and how they can be used in your code. It helps keep your code clean, prevents errors, and ensures that variables don’t accidentally interfere with each other in unexpected ways.

// Global scope
let globalVar = "I am global"; // Global variable
const globalConst = "I am a global constant"; // Global constant

console.log(globalVar); // Output: I am global
console.log(globalConst); // Output: I am a global constant

// Block scope
{
  let blockVar = "I am block scoped"; // Block-scoped variable
  const blockConst = "I am block scoped constant"; // Block-scoped constant

  console.log(blockVar); // Output: I am block scoped
  console.log(blockConst); // Output: I am block scoped constant
}

// Trying to access block-scoped variables outside their block
// Uncommenting the lines below will result in ReferenceError
// console.log(blockVar);   // Error: blockVar is not defined
// console.log(blockConst); // Error: blockConst is not defined

Naming variables and avoiding pitfalls

In JavaScript, once you've declared a variable or constant using let or const, you can't reuse that same name within the same scope—it'll throw a syntax error. Imagine you've already named a variable score in a particular block of code; trying to declare another score in that same block will result in an error. However, if you move into a nested block (a block within another block), you technically can declare a new score variable, but this practice can be confusing and is best avoided to keep your code clear and bug-free.

JavaScript is also quite flexible with what you can store in a variable. Unlike some other languages, JavaScript variables don't have a fixed type. This means you can store a number in a variable and later change it to hold a string. For example, you might start with:

let data = 42; // 'data' holds a number
data = "Hello"; // Now 'data' holds a string

While JavaScript allows this, it's generally not a good idea. Changing the type of data stored in a variable can make your code harder to understand and debug. It's better to keep your variable types consistent to avoid unexpected behavior and to make your code easier to follow.

Back in the days of “var”, JavaScript variables before ES6

Before ES6 introduced let and const, var was the only way to declare a variable in JavaScript. But while var might look similar to let in terms of syntax, it behaves quite differently under the hood. Here are the key difference, pay attention now as this is a common question that gets asked in job interviews:

  • variables declared with var don't have block scope. This means that even if you declare a var inside a loop or an if statement, it's accessible throughout the entire function that contains it—not just within the curly braces where you defined it. This can lead to unexpected behavior if you're not careful, especially when working with deeply nested code.
  • When you declare a var outside any function, it creates a global variable. But here's the catch: global variables created with var are added as properties to the global object (like window in browsers), which isn't the case with let or const. This distinction can have significant implications for your code, especially in larger projects where global namespace pollution can become an issue.
  • Another quirky aspect of var is that you can declare the same variable name multiple times within the same scope. This isn't usually a great idea, as it can make your code harder to follow and debug. But because var is function-scoped (not block-scoped), redeclaring variables within the same function is surprisingly common in older JavaScript code.
  • One of the most unusual and often confusing features of var is something called "hoisting." When you declare a variable with var, JavaScript essentially "hoists" the declaration to the top of the function. This means you can use the variable anywhere in the function, even before the line where you actually declare it. The catch? If the initialization hasn’t happened yet, the variable's value will be undefined. This can lead to subtle bugs that are tricky to track down, especially in larger functions.
// Example of var with scope
function varScopeExample() {
  if (true) {
    var varInIf = "I am visible throughout the function"; // var does not have block scope
  }
  console.log(varInIf); // Output: I am visible throughout the function
}

varScopeExample();

// Example of var creating a global property
var globalVar = "I am a global variable";
console.log(window.globalVar); // Output: I am a global variable (in a browser environment)

// Example of var redeclaration
function varRedeclarationExample() {
  var redeclareVar = "First declaration";
  var redeclareVar = "Second declaration"; // Allowed with var
  console.log(redeclareVar); // Output: Second declaration
}

varRedeclarationExample();

// Example of var hoisting
function varHoistingExample() {
  console.log(hoistedVar); // Output: undefined (due to hoisting)
  var hoistedVar = "I am hoisted";
  console.log(hoistedVar); // Output: I am hoisted
}

varHoistingExample();

// Comparison with let and const
function letConstComparison() {
  if (true) {
    let blockScopedLet = "I am block scoped"; // Block scope
    const blockScopedConst = "I am also block scoped"; // Block scope
    console.log(blockScopedLet); // Output: I am block scoped
    console.log(blockScopedConst); // Output: I am also block scoped
  }
  // Uncommenting the lines below will result in ReferenceError
  // console.log(blockScopedLet); // Error: blockScopedLet is not defined
  // console.log(blockScopedConst); // Error: blockScopedConst is not defined
}

letConstComparison();

Interview tip: In job interviews, you might be asked to explain the differences between var, let, and const. Be prepared to discuss scope, hoisting, and global object behavior. The interviewer may ask for examples or scenarios where using let or const would prevent bugs that might occur with var. Make sure you highlight how let and const offer more predictable and secure behavior in modern JavaScript. In contrast, let and const don't hoist in the same way. If you try to use a let or const variable before it’s declared, you'll get an error, helping you avoid those sneaky bugs that can arise from using variables too early. In short, while var was the workhorse of JavaScript for many years, it's got some quirks that can trip you up. With the introduction of let and const, many of these issues are now avoidable, making your code more predictable and easier to debug.

Study Style Notes

Variables and Constants: The Basics

  • Identifiers: Names used in programming to represent values, making it easier to refer to and manipulate these values in your code.
  • Variables (let): Bind a name to a value, allowing it to change over time. Useful when you expect the value to be reassigned during the program's execution.
  • Constants (const): Bind a name to a value that should remain the same throughout the code. Once assigned, a constant’s value cannot be changed.
  • Declaration:
    • Before using a variable or constant, you must declare it using let for variables or const for constants.
    • For let, you can declare a variable without assigning a value, leaving it undefined until you do so.
    • For const, you must assign an initial value at the time of declaration.

Variable Scope

  • Scope: The area in your code where a variable or constant is recognized and can be accessed.
  • Block Scope: Variables and constants declared with let and const are only accessible within the block of code (e.g., within {}) where they are declared. Outside of these curly braces, the variables are no longer accessible.
  • Loop Scope: In loops like for, for/in, or for/of, the variables declared in the loop’s initialization are only accessible within the loop's body.
  • Global Scope: If you declare a variable or constant outside of any blocks, it’s global and can be accessed from anywhere within the file.

Naming Variables and Avoiding Pitfalls

  • Unique Names: You can't reuse the same name for variables or constants in the same scope; attempting to do so will cause a syntax error.
  • Nested Scopes: While you can technically reuse names in nested blocks, it's best avoided to prevent confusion.
  • Dynamic Typing: JavaScript is flexible with variable types, allowing you to store different types of values (e.g., a number, then a string) in the same variable. However, this can lead to confusion and bugs, so it’s better to keep the data type consistent.

Pre-ES6 Variables: The var Keyword

  • No Block Scope: Variables declared with var do not have block scope; they are function-scoped. This means that a var declared within a loop or if-statement is accessible throughout the entire function, not just within the curly braces.
  • Global Variables with var: Declaring var outside any function creates a global variable that becomes a property of the global object (like window in browsers). This can lead to issues in larger projects due to global namespace pollution.
  • Redeclaration: You can redeclare the same var multiple times in the same scope, which can make code difficult to follow and debug.
  • Hoisting: JavaScript "hoists" var declarations to the top of their scope. This means you can use a var before it's declared, but its value will be undefined if the initialization hasn’t happened yet.
  • Avoiding var: With let and const, you avoid many pitfalls associated with var, such as accidental global variables, unexpected hoisting, and unclear code due to redeclarations.