Chapter 8: Modern JavaScript: ES6 and Beyond
Tags: javascript
Published: 2023-05-30 12:58:13

## 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.


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

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


```javascript
if (true) {
    var myVar = 'Hello, world!';
}
console.log(myVar); // Outputs: 'Hello, world!'
```

**`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.


```javascript
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
```

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.


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

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.


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

myObject.myMethod(); // Outputs: 'I am an object property!'
```

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


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

myObject.myMethod(); // Outputs: undefined
```

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.


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

myObject.myMethod(); // Outputs: 'I am an object property!'
```

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.


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

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.


```javascript
let arr1 = [1, 2, 3];
let arr2 = [...arr1, 4, 5, 6]; // arr2 is now [1, 2, 3, 4, 5, 6]
```

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


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

console.log(sum(1, 2, 3, 4)); // Outputs: 10
```

## Destructuring and Default Parameters

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


```javascript
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
```

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


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

greet('Alice'); // Outputs: "Hello, Alice!"
greet(); // Outputs: "Hello, World!"
```

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.