DEV Community

shunku
shunku

Posted on

Chapter 8: Modern JavaScript: ES6 and Beyond

Let, Const and Block Scope

In JavaScript, the scope of a variable defines the portion of the code where the variable can be accessed. Prior to ES6, JavaScript had only two types of scope: Global Scope and Function Scope. ES6 introduced Block Scope with the new let and const keywords, which are improvements over the function scoped var.

var and Function Scope

Variables declared with var are function scoped. This means that if a variable is declared anywhere within a function, it's accessible everywhere in that function, even before it's been declared due to a feature called hoisting.

function example() {
    console.log(myVar); // Outputs: undefined (Not an error)
    var myVar = 'Hello, world!';
    console.log(myVar); // Outputs: 'Hello, world!'
}
Enter fullscreen mode Exit fullscreen mode

Also, var does not respect block scope -- a block being what's inside any pair of curly braces {}.

if (true) {
    var myVar = 'Hello, world!';
}
console.log(myVar); // Outputs: 'Hello, world!'
Enter fullscreen mode Exit fullscreen mode

let and const with Block Scope

Unlike var, variables declared with let and const are block scoped. This means that they exist only within the block in which they were declared, as well as any nested blocks.

if (true) {
    let myLetVar = 'Hello, world!';
    const myConstVar = 'Hello again, world!';
}
console.log(myLetVar); // ReferenceError: myLetVar is not defined
console.log(myConstVar); // ReferenceError: myConstVar is not defined
Enter fullscreen mode Exit fullscreen mode

In the above code, myLetVar and myConstVar are not accessible outside the if block.

Also, let and const are not hoisted like var. When trying to access them before they're declared, JavaScript throws an error.

function example() {
    console.log(myLetVar); // ReferenceError: Cannot access 'myLetVar' before initialization
    let myLetVar = 'Hello, world!';
}
Enter fullscreen mode Exit fullscreen mode

Understanding these differences and the concept of block scope is crucial for writing predictable, bug-free JavaScript code.

Arrow Functions

Arrow functions not only provide a more concise syntax to write functions, but they also have some differences in how they handle the this keyword.

In JavaScript, this is a special keyword that's set at the time of a function's execution, and what it refers to depends on how the function was called.

this in Traditional Function

In traditional function calls, this is set to the object that the function is a property of — also known as the calling object.

let myObject = {
  property: 'I am an object property!',
  myMethod: function() {
    console.log(this.property);
  }
};

myObject.myMethod(); // Outputs: 'I am an object property!'
Enter fullscreen mode Exit fullscreen mode

But this can lead to unexpected results when this is used inside a callback function:

let myObject = {
  property: 'I am an object property!',
  myMethod: function() {
    setTimeout(function() {
      console.log(this.property);
    }, 1000);
  }
};

myObject.myMethod(); // Outputs: undefined
Enter fullscreen mode Exit fullscreen mode

Here, this inside the setTimeout callback function refers to the global window object, not myObject.

this in Arrow Function

Arrow functions handle this differently. They don't have their own this. Instead, they inherit this from the surrounding lexical context.

let myObject = {
  property: 'I am an object property!',
  myMethod: function() {
    setTimeout(() => {
      console.log(this.property);
    }, 1000);
  }
};

myObject.myMethod(); // Outputs: 'I am an object property!'
Enter fullscreen mode Exit fullscreen mode

In this example, the arrow function inherits this from myMethod, so this.property correctly refers to myObject.property.

This makes arrow functions particularly useful when working with methods that use callbacks, such as event listeners or timer functions. It's one of the many reasons arrow functions are popular in modern JavaScript development.

Template Strings, Spread and Rest Operators

Template strings allow you to embed expressions (variables, computations, etc.) directly into strings using ${} syntax.

let name = 'Alice';
console.log(`Hello, ${name}!`); // Outputs: "Hello, Alice!"
Enter fullscreen mode Exit fullscreen mode

The spread operator (...) allows an iterable such as an array or string to be expanded in places where zero or more arguments or elements are expected.

let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5, 6]; // arr2 is now [1, 2, 3, 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

The rest operator (...) is used to represent an indefinite number of arguments as an array.

function sum(...nums) {
  return nums.reduce((total, num) => total + num, 0);
}

console.log(sum(1, 2, 3, 4)); // Outputs: 10
Enter fullscreen mode Exit fullscreen mode

Destructuring and Default Parameters

Destructuring allows you to unpack values from arrays, or properties from objects, into distinct variables.

let [a, b] = [1, 2];
console.log(a); // Outputs: 1
console.log(b); // Outputs: 2

let { name, age } = { name: 'Alice', age: 25 };
console.log(name); // Outputs: "Alice"
console.log(age); // Outputs: 25
Enter fullscreen mode Exit fullscreen mode

Default parameters allow you to specify default values for function parameters.

function greet(name = 'World') {
  console.log(`Hello, ${name}!`);
}

greet('Alice'); // Outputs: "Hello, Alice!"
greet(); // Outputs: "Hello, World!"
Enter fullscreen mode Exit fullscreen mode

These modern JavaScript features help make your code more readable, flexible, and expressive. They're widely used in modern web development, so it's important to understand and become comfortable with them.

Top comments (0)