Detailed Research Notes on JavaScript
ES6+ Features
This document provides detailed research notes on key features introduced in ECMAScript
2015 (ES6) and subsequent versions (ES7, ES8, ES9, etc.), often collectively referred to as
ES6+. These features have significantly enhanced JavaScript's capabilities, readability, and
developer experience.
1. let and const Declarations
ES6 introduced let and const as alternatives to var for variable declaration, providing
block-scoping and improved control over variable immutability.
● let: Declares a block-scoped local variable, optionally initializing it to a value. It can be
reassigned.
● const: Declares a block-scoped, read-only named constant. It must be initialized at
declaration and cannot be reassigned.
// Using let
let count = 10;
if (true) {
let count = 20; // Different 'count' variable, block-scoped
console.log(count); // Output: 20
}
console.log(count); // Output: 10
// Using const
const PI = 3.14159;
// PI = 3.14; // This would cause an error: Assignment to constant
variable.
const user = { name: 'Alice' };
user.name = 'Bob'; // This is allowed: modifying properties of a const
object
console.log(user); // Output: { name: 'Bob' }
// user = { name: 'Charlie' }; // This would cause an error:
Reassignment of const variable
2. Arrow Functions (=>)
Arrow functions provide a more concise syntax for writing function expressions. They also have
a lexical this binding, meaning this is determined by the surrounding scope, not by how the
function is called.
// Traditional function expression
const addTraditional = function(a, b) {
return a + b;
};
console.log(addTraditional(2, 3)); // Output: 5
// Arrow function (concise syntax for single expression)
const addArrow = (a, b) => a + b;
console.log(addArrow(2, 3)); // Output: 5
// Arrow function with block body
const greet = (name) => {
const greeting = `Hello, ${name}!`;
return greeting;
};
console.log(greet('World')); // Output: Hello, World!
// Lexical 'this' example
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++; // 'this' refers to the Timer instance
console.log(this.seconds);
}, 1000);
}
// const timer = new Timer(); // Uncomment to see it in action
3. Template Literals (Template Strings)
Template literals allow for easier string interpolation and multiline strings using backticks (`).
const name = 'Alice';
const age = 30;
// Traditional string concatenation
const messageOld = 'Hello, my name is ' + name + ' and I am ' + age +
' years old.';
console.log(messageOld);
// Using template literals
const messageNew = `Hello, my name is ${name} and I am ${age} years
old.`;
console.log(messageNew);
// Multiline strings
const multilineString = `
This is a string
that spans multiple
lines.
`;
console.log(multilineString);
4. Destructuring Assignment
Destructuring assignment allows you to unpack values from arrays or properties from objects
into distinct variables.
Array Destructuring
const colors = ['red', 'green', 'blue'];
// Destructuring array elements
const [firstColor, secondColor, thirdColor] = colors;
console.log(firstColor); // Output: red
console.log(secondColor); // Output: green
// Skipping elements
const [,, lastColor] = colors;
console.log(lastColor); // Output: blue
// Swapping variables easily
let a = 1;
let b = 2;
[a, b] = [b, a];
console.log(a, b); // Output: 2 1
Object Destructuring
const person = {
firstName: 'John',
lastName: 'Doe',
age: 40
};
// Destructuring object properties
const { firstName, age } = person;
console.log(firstName); // Output: John
console.log(age); // Output: 40
// Renaming properties during destructuring
const { firstName: fName, lastName: lName } = person;
console.log(fName); // Output: John
console.log(lName); // Output: Doe
// Destructuring with default values
const { city = 'New York', age: personAge } = person;
console.log(city); // Output: New York (since 'city' not in
person)
console.log(personAge); // Output: 40
5. Spread (...) and Rest (...) Operators
The ... syntax serves two distinct purposes:
● Spread Operator: Expands an iterable (like an array or string) into individual elements.
Useful for array literals, function calls, and object literals.
● Rest Parameters: Collects an indefinite number of arguments into an array. Used in
function parameter lists.
Spread Operator Examples
// Spreading arrays
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // Creates a new array [1, 2, 3, 4, 5]
console.log(arr2);
// Spreading objects (ES8)
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // Creates a new object { a: 1, b: 2,
c: 3 }
console.log(obj2);
// Passing array elements as function arguments
const numbers = [10, 20, 30];
function sum(x, y, z) {
return x + y + z;
}
console.log(sum(...numbers)); // Output: 60
Rest Parameters Example
function collectArgs(firstArg, ...restOfArgs) {
console.log('First argument:', firstArg);
console.log('Rest of arguments:', restOfArgs); // restOfArgs is an
array
}
collectArgs(1, 2, 3, 4, 5);
// Output:
// First argument: 1
// Rest of arguments: [2, 3, 4, 5]
collectArgs('hello');
// Output:
// First argument: hello
// Rest of arguments: []
6. Classes
ES6 introduced class syntax, which is syntactic sugar over JavaScript's existing
prototype-based inheritance. It provides a cleaner and more familiar way to create objects and
handle inheritance.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound.`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name); // Call the parent constructor
this.breed = breed;
}
speak() {
console.log(`${this.name} barks!`);
}
fetch() {
console.log(`${this.name} fetches the ball.`);
}
}
const myAnimal = new Animal('Generic Animal');
myAnimal.speak(); // Output: Generic Animal makes a sound.
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // Output: Buddy barks! (overridden method)
myDog.fetch(); // Output: Buddy fetches the ball.
7. Modules (import / export)
ES6 introduced a standardized module system, allowing developers to break down code into
reusable units and manage dependencies.
math.js (Module to export)
// math.js
export const PI = 3.14159;
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// Default export
export default function multiply(a, b) {
return a * b;
}
app.js (Module to import)
// app.js
// Import named exports
import { PI, add } from './math.js';
// Import all named exports as an object
import * as MathUtils from './math.js';
// Import default export (can be named anything)
import multiplyNumbers from './math.js';
console.log(PI); // Output: 3.14159
console.log(add(5, 3)); // Output: 8
console.log(MathUtils.subtract(10, 4)); // Output: 6
console.log(multiplyNumbers(2, 6)); // Output: 12
Note: Module import/export requires a build step (like Webpack or Parcel) or a browser that
supports ES Modules (using <script type="module">).
8. Promises
Promises provide a cleaner way to handle asynchronous operations, avoiding "callback hell." A
Promise represents the eventual completion (or failure) of an asynchronous operation and its
resulting value.
function fetchData() {
return new Promise((resolve, reject) => {
// Simulate an asynchronous operation (e.g., fetching data
from an API)
setTimeout(() => {
const success = true; // Change to false to simulate an
error
if (success) {
resolve('Data successfully fetched!');
} else {
reject('Error fetching data.');
}
}, 2000); // 2-second delay
});
}
fetchData()
.then((data) => {
console.log('Success:', data);
return 'Processed: ' + data; // Chain another .then
})
.then((processedData) => {
console.log('Second then:', processedData);
})
.catch((error) => {
console.error('Error:', error);
})
.finally(() => {
console.log('Operation finished (resolved or rejected).');
});
console.log('Fetching data...'); // This will log immediately
9. Async/Await (ES2017 - ES8)
async and await are syntactic sugar built on top of Promises, making asynchronous code look
and behave more like synchronous code, greatly improving readability.
● async function: A function declared with async keyword automatically returns a Promise.
● await expression: Can only be used inside an async function. It pauses the execution of
the async function until the Promise it's waiting for settles (resolves or rejects).
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('Resolved after 2 seconds');
}, 2000);
});
}
async function asyncCall() {
console.log('Calling...');
const result = await resolveAfter2Seconds(); // Pause here until
Promise resolves
console.log(result); // Output: Resolved after 2 seconds
// Further synchronous-looking code executes after the await
return 'Async call complete';
}
asyncCall().then(message => console.log(message));
console.log('This logs immediately after asyncCall is invoked.');
Error Handling with async/await
function rejectAfter1Second() {
return new Promise((_, reject) => {
setTimeout(() => {
reject('Error: Something went wrong!');
}, 1000);
});
}
async function handleAsyncError() {
try {
console.log('Attempting to fetch...');
const data = await rejectAfter1Second();
console.log('Data:', data); // This line will not be reached
} catch (error) {
console.error('Caught an error:', error); // Output: Caught an
error: Error: Something went wrong!
} finally {
console.log('Cleanup or final actions.');
}
}
handleAsyncError();
10. Default Parameters
Default parameters allow you to initialize function parameters with default values if no value or
undefined is passed.
function greetPerson(name = 'Guest', greeting = 'Hello') {
console.log(`${greeting}, ${name}!`);
}
greetPerson('Alice'); // Output: Hello, Alice!
greetPerson(); // Output: Hello, Guest!
greetPerson('Bob', 'Hi'); // Output: Hi, Bob!
greetPerson(undefined, 'Hey'); // Output: Hey, Guest!
11. Enhanced Object Literals
ES6 introduced several enhancements to object literals, making them more concise and
powerful.
● Property Shorthand: If a variable name is the same as the desired property name, you
can just list the variable.
● Method Shorthand: A more concise syntax for defining methods.
● Computed Property Names: Allows using an expression for a property name.
const userName = 'Charlie';
const userAge = 25;
const role = 'admin';
const userProfile = {
// Property Shorthand
userName,
userAge,
// Method Shorthand
greet() {
console.log(`Hello, ${this.userName}!`);
},
// Computed Property Names
[role + 'Id']: 123,
// Traditional property
isActive: true
};
console.log(userProfile);
// Output: { userName: 'Charlie', userAge: 25, adminId: 123, isActive:
true, greet: [Function: greet] }
userProfile.greet(); // Output: Hello, Charlie!
12. Iterators and Generators (Brief Overview)
● Iterators: Objects that define a sequence and a return value upon its termination. They
implement the next() method, which returns an object with value and done properties.
● Generators: Functions that can be paused and resumed, yielding (returning) multiple
values over time. They are defined using function* and use the yield keyword. They are a
powerful way to create custom iterators.
// Simple Generator Function
function* idGenerator() {
let id = 1;
while (true) {
yield id++;
}
}
const gen = idGenerator();
console.log(gen.next().value); // Output: 1
console.log(gen.next().value); // Output: 2
console.log(gen.next().value); // Output: 3
13. Map and Set
New built-in data structures for more efficient data handling.
● Map: A collection of key-value pairs where keys can be of any data type (unlike plain
objects where keys are always strings or symbols). Maintains insertion order.
const myMap = new Map();
myMap.set('name', 'Alice');
myMap.set(1, 'number one');
myMap.set(true, 'boolean key');
myMap.set({}, 'object key'); // Objects can be keys
console.log(myMap.get('name')); // Output: Alice
console.log(myMap.size); // Output: 4
console.log(myMap.has(1)); // Output: true
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
● Set: A collection of unique values. Any duplicate values added to a Set are ignored.
Maintains insertion order.
const mySet = new Set();
mySet.add(1);
mySet.add(5);
mySet.add('text');
mySet.add(1); // Duplicate, ignored
console.log(mySet.size); // Output: 3
console.log(mySet.has(5)); // Output: true
console.log(mySet.has(10)); // Output: false
const numbersWithDuplicates = [1, 2, 2, 3, 4, 4, 5];
const uniqueNumbers = [...new Set(numbersWithDuplicates)];
console.log(uniqueNumbers); // Output: [1, 2, 3, 4, 5]