ES6+ Features: Spread, Rest, Template Literals, and Modules
Course: React Native Training
Topic: ES6+ Features: Spread, Rest, Template Literals, and Modules
Course Link: React Native Training Course
Demo Link: Register for a Free Course Demo
Official Website: JustAcademy
1. Introduction to ES6+ Features
ES6, also known as ECMAScript 2015, introduced several important improvements to JavaScript. These features make code more readable, reusable, concise, and easier to maintain. Later ECMAScript versions introduced additional improvements that are commonly grouped under the term ES6+.
ES6+ features are particularly useful in React Native development because applications frequently work with arrays, objects, component props, state, API responses, and reusable modules.
1.1 What Is ECMAScript?
ECMAScript is the language specification on which JavaScript is based. JavaScript engines implement ECMAScript features to provide the language capabilities developers use when building web and mobile applications.
1.2 Important ES6+ Features
| Feature |
Purpose |
Example |
| Spread operator |
Expands elements or properties from arrays and objects. |
[...numbers] |
| Rest parameter |
Collects multiple arguments into an array. |
function sum(...values) |
| Template literals |
Embed expressions and create multiline strings. |
`Hello ${name}` |
| Modules |
Organize code into reusable files. |
export and import |
| Destructuring |
Extract values from arrays and objects. |
const {name} = user |
| Arrow functions |
Write concise function expressions. |
const add = (a, b) => a + b |
| Optional chaining |
Safely access potentially missing properties. |
user?.profile?.name |
| Default parameters |
Provide default values for function parameters. |
function greet(name = "Guest") |
2. Learning Objectives
After completing this topic, learners should be able to:
- Understand the purpose of modern JavaScript features.
- Use the spread operator with arrays and objects.
- Use rest parameters and distinguish them from spread syntax.
- Create dynamic strings using template literals.
- Understand named exports, default exports, and imports.
- Organize React Native applications into reusable modules.
- Use spread syntax to update arrays and objects immutably.
- Apply ES6+ features when working with React Native props, state, and API data.
- Recognize common mistakes and follow modern JavaScript best practices.
3. The Spread Operator
The spread operator uses three dots (...) to expand the elements of an iterable or the enumerable properties of an object into another expression. It is commonly used with arrays, objects, and function calls.
3.1 Spread Operator with Arrays
The spread operator can expand the elements of an array into a new array.
const numbers = [10, 20, 30];
const copiedNumbers = [...numbers];
console.log(copiedNumbers);
// [10, 20, 30]
Here, ... expands the elements of numbers into the new array. The new array is a different array from the original.
3.2 Combining Arrays
Spread syntax makes it easy to combine multiple arrays.
const first = [1, 2, 3];
const second = [4, 5, 6];
const combined = [...first, ...second];
console.log(combined);
// [1, 2, 3, 4, 5, 6]
You can also insert additional values while combining arrays.
const weekdays = ["Monday", "Tuesday"];
const allDays = ["Sunday", ...weekdays, "Wednesday"];
console.log(allDays);
// ["Sunday", "Monday", "Tuesday", "Wednesday"]
3.3 Adding Elements to an Array
Spread syntax can create a new array containing existing elements and additional values.
const fruits = ["Apple", "Banana"];
const updatedFruits = [...fruits, "Orange"];
console.log(updatedFruits);
// ["Apple", "Banana", "Orange"]
This approach is useful when adding items to React Native state without directly modifying the original array.
3.4 Spread Operator with Objects
Object spread copies enumerable own properties from an object into a new object.
const user = {
name: "Aarav",
age: 24
};
const copiedUser = { ...user };
console.log(copiedUser);
// { name: "Aarav", age: 24 }
3.5 Updating Object Properties
Spread syntax is commonly used to update a particular property while retaining other properties.
const user = {
name: "Aarav",
age: 24,
city: "Mumbai"
};
const updatedUser = {
...user,
age: 25
};
console.log(updatedUser);
// { name: "Aarav", age: 25, city: "Mumbai" }
The original object remains unchanged. If a property is specified more than once, the value from the later occurrence takes precedence.
const settings = {
theme: "light",
fontSize: 14
};
const updatedSettings = {
...settings,
theme: "dark"
};
console.log(updatedSettings);
// { theme: "dark", fontSize: 14 }
3.6 Spread Operator in Function Calls
Spread syntax can pass the elements of an array as individual function arguments.
function add(a, b, c) {
return a + b + c;
}
const values = [10, 20, 30];
console.log(add(...values));
// 60
3.7 Spread Operator with Strings
Strings are iterable, so spread syntax can expand a string into individual characters.
const word = "React";
const characters = [...word];
console.log(characters);
// ["R", "e", "a", "c", "t"]
3.8 Shallow Copy Limitation
Spread syntax creates a shallow copy, not a deep copy. Nested objects remain shared references unless they are copied separately.
const original = {
name: "Aarav",
address: {
city: "Mumbai"
}
};
const copy = { ...original };
console.log(copy !== original);
// true
console.log(copy.address === original.address);
// true
To update a nested object without changing the original nested reference, copy each level that needs to change.
const updated = {
...original,
address: {
...original.address,
city: "Pune"
}
};
console.log(updated.address.city);
// Pune
console.log(original.address.city);
// Mumbai
3.9 Advantages of Spread Syntax
- Creates new arrays and objects conveniently.
- Combines arrays and object properties in a readable way.
- Supports immutable state updates.
- Passes array elements to functions.
- Reduces repetitive copying code.
4. The Rest Parameter
The rest parameter also uses three dots (...), but it collects multiple values into a single array. It is commonly used in function definitions when the number of arguments is not fixed.
4.1 Basic Rest Parameter Example
function showNumbers(...numbers) {
console.log(numbers);
}
showNumbers(10, 20, 30, 40);
// [10, 20, 30, 40]
The rest parameter collects all remaining arguments into an array named numbers.
4.2 Calculating a Sum
function sum(...numbers) {
return numbers.reduce((total, number) => total + number, 0);
}
console.log(sum(10, 20));
// 30
console.log(sum(10, 20, 30, 40));
// 100
The function works with different numbers of arguments because the rest parameter collects them into an array.
4.3 Rest Parameter with Regular Parameters
Regular parameters can appear before a rest parameter.
function introduce(greeting, ...names) {
console.log(greeting);
console.log(names);
}
introduce("Welcome", "Aarav", "Priya", "Rohan");
// Welcome
// ["Aarav", "Priya", "Rohan"]
The rest parameter must be the last parameter in a function definition.
4.4 Rest Syntax in Array Destructuring
Rest syntax can collect the remaining array elements during destructuring.
const numbers = [10, 20, 30, 40, 50];
const [first, second, ...remaining] = numbers;
console.log(first);
// 10
console.log(second);
// 20
console.log(remaining);
// [30, 40, 50]
4.5 Rest Syntax in Object Destructuring
In object destructuring, rest syntax collects the properties that have not already been extracted.
const employee = {
name: "Priya",
role: "Developer",
department: "Engineering",
city: "Mumbai"
};
const { name, ...otherDetails } = employee;
console.log(name);
// Priya
console.log(otherDetails);
// { role: "Developer", department: "Engineering", city: "Mumbai" }
4.6 Rest Parameter Rules
- A function can have only one rest parameter.
- The rest parameter must appear last in the parameter list.
- The collected values are stored in an array.
- Rest parameters are useful for flexible function APIs.
- Rest syntax in destructuring collects remaining elements or properties.
5. Spread vs Rest Operator
Spread and rest use the same three-dot syntax, but their purposes are different. The surrounding syntax determines how the dots behave.
| Point |
Spread |
Rest |
| Purpose |
Expands values. |
Collects values. |
| Common usage |
Array literals, object literals, function calls. |
Function parameters and destructuring assignments. |
| Result |
Individual elements or properties are inserted into a target expression. |
Remaining values are collected into an array or object. |
| Example |
[...items] |
function test(...items) |
5.1 Comparison Example
// Spread: expands an array
const numbers = [1, 2, 3];
const copy = [...numbers];
// Rest: collects function arguments
function display(...values) {
console.log(values);
}
display(1, 2, 3);
Remember: spread expands an existing collection, while rest gathers multiple values into a collection.
6. Template Literals
Template literals are string literals enclosed in backticks (`). They allow developers to embed JavaScript expressions directly into strings using ${...}. They also support multiline strings without requiring special newline escape sequences.
6.1 Traditional String Concatenation
const name = "Aarav";
const message = "Hello, " + name + "!";
console.log(message);
// Hello, Aarav!
6.2 Using Template Literals
const name = "Aarav";
const message = `Hello, ${name}!`;
console.log(message);
// Hello, Aarav!
Template literals make dynamic strings easier to read, especially when a string contains multiple variables.
6.3 Embedding Expressions
Any valid JavaScript expression can be evaluated inside the interpolation syntax.
const a = 10;
const b = 20;
const message = `The total is ${a + b}.`;
console.log(message);
// The total is 30.
6.4 Using Functions Inside Template Literals
function getFullName(firstName, lastName) {
return `${firstName} ${lastName}`;
}
const message = `Welcome, ${getFullName("Priya", "Sharma")}!`;
console.log(message);
// Welcome, Priya Sharma!
6.5 Multiline Strings
Template literals can contain line breaks directly.
const details = `Name: Priya
Course: React Native
Status: Enrolled`;
console.log(details);
This is more convenient than joining several quoted strings with newline characters.
6.6 Template Literals for API URLs
Template literals can build URLs using dynamic values. Always validate or encode user-supplied query values when constructing URLs.
const userId = 42;
const apiUrl = `https://api.example.com/users/${userId}`;
console.log(apiUrl);
// https://api.example.com/users/42
6.7 Template Literals in React Native
Template literals are useful when displaying dynamic text in React Native components.
import React from "react";
import { View, Text } from "react-native";
export default function WelcomeScreen() {
const name = "Aarav";
const course = "React Native";
return (
<View>
<Text>{`Hello, ${name}!`}</Text>
<Text>{`Welcome to ${course} training.`}</Text>
</View>
);
}
6.8 Displaying Prices and Calculated Values
const product = "Headphones";
const price = 1999;
const quantity = 2;
const total = price * quantity;
const summary = `Product: ${product}
Price: ₹${price}
Quantity: ${quantity}
Total: ₹${total}`;
console.log(summary);
6.9 Common Template Literal Mistakes
- Using single quotes instead of backticks when interpolation is required.
- Writing
${name} outside a template literal and expecting substitution.
- Forgetting that expressions inside
${...} are evaluated as JavaScript.
- Embedding untrusted text into generated HTML without appropriate escaping in environments where HTML is interpreted.
7. JavaScript Modules
Modules allow developers to split a large application into smaller files. Each module can define variables, functions, components, constants, or classes and export the functionality that other files need.
React Native projects commonly use JavaScript modules to separate screens, reusable components, API services, utility functions, and configuration values.
7.1 Why Modules Are Important
- Organize application code into logical files.
- Promote code reuse across screens and components.
- Make testing and debugging easier.
- Reduce unnecessary duplication.
- Make large applications easier to maintain.
- Clarify dependencies between files.
7.2 Named Exports
A named export allows a module to export one or more specifically named values.
File: mathUtils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export function multiply(a, b) {
return a * b;
}
Import named exports using their exported names inside curly braces.
File: App.js
import { add, subtract, multiply } from "./mathUtils";
console.log(add(10, 5));
// 15
console.log(subtract(10, 5));
// 5
console.log(multiply(10, 5));
// 50
7.3 Default Exports
A module can have one default export. Default exports are often used when a file represents one main component or function.
File: greeting.js
export default function greeting(name) {
return `Hello, ${name}!`;
}
File: App.js
import greeting from "./greeting";
console.log(greeting("Aarav"));
// Hello, Aarav!
The name used when importing a default export can differ from the original function name.
7.4 Named Export vs Default Export
| Feature |
Named Export |
Default Export |
| Number per module |
Can export multiple named values. |
One default export per module. |
| Import syntax |
Usually uses curly braces. |
Does not use curly braces for the default binding. |
| Import name |
Must match the exported name unless aliased. |
Can be chosen by the importer. |
| Example export |
export const API_URL = "..." |
export default App |
| Example import |
import { API_URL } from "./config" |
import App from "./App" |
7.5 Renaming Named Imports
You can use the as keyword to give an imported named export a local alias.
// File: calculations.js
export const add = (a, b) => a + b;
// File: App.js
import { add as calculateSum } from "./calculations";
console.log(calculateSum(5, 7));
// 12
7.6 Importing Multiple Exports
// File: userUtils.js
export const formatName = (name) => name.trim();
export const isValidName = (name) => name.trim().length > 0;
// File: App.js
import { formatName, isValidName } from "./userUtils";
console.log(formatName(" Priya "));
console.log(isValidName("Priya"));
7.7 Exporting at the End of a File
You can define functions or constants first and export them together at the end of the module.
const API_VERSION = "v1";
function getApiVersion() {
return API_VERSION;
}
export { API_VERSION, getApiVersion };
7.8 Re-exporting Values
A module can re-export functionality from another module. This pattern is useful when creating a central export file, often called a barrel file.
// File: components/Button.js
export default function Button() {
return null;
}
// File: components/index.js
export { default as Button } from "./Button";
Another file can import the component using the central file:
import { Button } from "./components";
Barrel files can simplify imports, but overly broad re-exports may make dependencies harder to trace or affect bundling in some project setups.
8. Creating Reusable Modules in React Native
A React Native application often separates UI components, screens, utility functions, and API logic into different files. Each file can export its reusable functionality for other modules to import.
8.1 Example Project Structure
MyReactNativeApp/
├── App.js
├── components/
│ └── WelcomeCard.js
├── screens/
│ └── HomeScreen.js
├── utils/
│ └── formatName.js
└── services/
└── api.js
8.2 Creating a Reusable Component
File: components/WelcomeCard.js
import React from "react";
import { View, Text } from "react-native";
export default function WelcomeCard({ name, course }) {
return (
<View>
<Text>{`Hello, ${name}!`}</Text>
<Text>{`Course: ${course}`}</Text>
</View>
);
}
8.3 Importing the Component
File: App.js
import React from "react";
import { View } from "react-native";
import WelcomeCard from "./components/WelcomeCard";
export default function App() {
return (
<View>
<WelcomeCard name="Aarav" course="React Native" />
</View>
);
}
In this example, the component uses object destructuring to read props and template literals to display dynamic text. The component is exported as the default export and imported into the main application file.
9. Using Spread Syntax with React Native Props
Spread syntax can pass multiple object properties as props to a component. This can reduce repetitive code when a component accepts many properties.
const user = {
name: "Priya",
role: "Developer"
};
// Example component usage:
<ProfileCard {...user} />
The example assumes a ProfileCard component that accepts name and role props.
function ProfileCard({ name, role }) {
return (
<View>
<Text>{name}</Text>
<Text>{role}</Text>
</View>
);
}
Only spread trusted, intentional properties into components. Avoid forwarding unrelated properties or values that a component should not receive.
10. Updating React Native State with Spread Syntax
React state should be updated by creating new arrays or objects rather than directly mutating the existing state. Spread syntax is a common technique for making these immutable updates.
10.1 Updating Object State
import React, { useState } from "react";
import { View, Text, Button } from "react-native";
export default function ProfileScreen() {
const [user, setUser] = useState({
name: "Aarav",
age: 24
});
const updateAge = () => {
setUser(previousUser => ({
...previousUser,
age: previousUser.age + 1
}));
};
return (
<View>
<Text>{`Name: ${user.name}`}</Text>
<Text>{`Age: ${user.age}`}</Text>
<Button title="Increase Age" onPress={updateAge} />
</View>
);
}
The functional state updater receives the latest previous state and returns a new object containing the updated age and the unchanged properties.
10.2 Adding an Item to Array State
const [tasks, setTasks] = useState(["Study JavaScript"]);
const addTask = () => {
setTasks(previousTasks => [
...previousTasks,
"Practice React Native"
]);
};
10.3 Updating an Item in Array State
const [tasks, setTasks] = useState([
{ id: "1", title: "Learn JavaScript", completed: false },
{ id: "2", title: "Learn React Native", completed: false }
]);
const completeTask = (taskId) => {
setTasks(previousTasks =>
previousTasks.map(task =>
task.id === taskId
? { ...task, completed: true }
: task
)
);
};
This example creates a new array and a new object for the changed task. The other task objects are retained because they do not need to change.
11. Combining ES6+ Features in One Example
The following example demonstrates template literals, object spread, array spread, destructuring, and reusable functions together.
const students = [
{ id: 1, name: "Aarav", score: 85 },
{ id: 2, name: "Priya", score: 92 }
];
function createStudent(name, ...scores) {
const average = scores.length
? scores.reduce((total, score) => total + score, 0) / scores.length
: 0;
return {
name,
average
};
}
const newStudent = createStudent("Rohan", 80, 90, 100);
const updatedStudents = [
...students,
{ id: 3, ...newStudent }
];
updatedStudents.forEach(({ name, score, average }) => {
const result = score ?? average;
console.log(`${name}: ${result}`);
});
The original student objects do not contain an average property, so this example uses the nullish coalescing operator to fall back to average. The destructured score and average variables may be undefined when the corresponding property is absent.
12. Common Errors and How to Fix Them
| Error |
Reason |
Solution |
| Incorrect spread syntax |
Using three dots where the expression does not support spread. |
Check the surrounding array, object, or function-call syntax. |
| Rest parameter not last |
A rest parameter is followed by another parameter. |
Move the rest parameter to the end. |
| Wrong import name |
A named import does not match the exported name. |
Check the export name or use an alias with as. |
| Wrong default import syntax |
A default export is imported using named-import syntax. |
Use a default import without curly braces. |
| Incorrect module path |
The relative path or filename is incorrect. |
Check the file location and path spelling. |
| State mutation |
An existing array or object is changed directly. |
Create a new array or object using spread syntax or other immutable operations. |
| Unexpected nested changes |
A shallow copy shares nested object references. |
Copy the nested objects that need to change. |
| Interpolation does not work |
Single or double quotes are used instead of backticks. |
Use backticks and place expressions inside ${...}. |
| Unexpected overwritten property |
A later object spread or property definition uses the same key. |
Check the property order and intended precedence. |
13. Best Practices
- Use spread syntax when creating a new array or object from existing data.
- Prefer immutable state updates in React Native components.
- Remember that spread creates a shallow copy rather than a deep copy.
- Use rest parameters when a function needs to accept a variable number of arguments.
- Choose clear names for rest parameters, such as
...items or ...values.
- Use template literals for readable dynamic strings and multiline text.
- Use named exports when a module exposes multiple related utilities.
- Use default exports when a file has one primary exported component or value.
- Keep import paths accurate and consistent with the project's file structure.
- Separate components, API services, constants, and utility functions into logical modules.
- Avoid spreading large objects into components when only a few properties are required.
- Use the project's configured JavaScript and React Native tooling to verify compatibility with supported syntax.
14. Practical Exercises
Exercise 1: Copy an Array
Create an array of five numbers and use spread syntax to copy it into another array. Confirm that the arrays are different references.
Exercise 2: Merge Arrays
Create two arrays of programming languages and combine them into one array using the spread operator.
Exercise 3: Update an Object
Create an object containing a person's name, age, and city. Create a new object with an updated city while preserving the other properties.
Exercise 4: Create a Flexible Sum Function
Write a function that accepts any number of numeric arguments using a rest parameter and returns their sum.
Exercise 5: Build a Template Literal
Create variables for a student's name, course, and score. Use a template literal to generate a formatted summary.
Exercise 6: Create a Utility Module
Create a file called stringUtils.js that exports two named functions: one that converts text to uppercase and another that counts characters. Import and test both functions in another file.
Exercise 7: Build a Reusable React Native Component
Create a reusable profile component that receives a name, role, and city as props. Use destructuring and template literals to display the details.
15. Mini Project: Student Profile Manager
Project objective: Create a small React Native application that displays student information and demonstrates spread syntax, rest parameters, template literals, and modules.
15.1 Requirements
- Create a reusable student card component.
- Store student information in an array of objects.
- Add a new student by creating a new array with spread syntax.
- Update a student's details without mutating the original object.
- Use template literals to display a personalized greeting.
- Move student-related utility functions into a separate module.
- Use named or default exports to share functionality between files.
15.2 Example Utility Module
File: utils/studentUtils.js
export function formatStudentName(name) {
return name.trim();
}
export function createStudent(name, ...subjects) {
return {
id: String(Date.now()),
name: formatStudentName(name),
subjects
};
}
15.3 Example Component
File: components/StudentCard.js
import React from "react";
import { View, Text } from "react-native";
export default function StudentCard({ student }) {
const { name, subjects = [] } = student;
return (
<View>
<Text>{`Student: ${name}`}</Text>
<Text>{`Subjects: ${subjects.join(", ")}`}</Text>
</View>
);
}
15.4 Expected Learning Outcome
After completing this project, learners should understand how modern JavaScript syntax improves code organization, enables reusable functionality, and supports safer updates to application data.
16. Interview Questions and Answers
Q1. What is the spread operator?
Answer: The spread operator uses ... to expand elements of an iterable or enumerable own properties of an object into a target expression.
Q2. What is the rest parameter?
Answer: The rest parameter collects the remaining function arguments into an array.
Q3. What is the main difference between spread and rest?
Answer: Spread expands a collection, while rest collects multiple values into a collection. Their syntax is the same, but their roles depend on context.
Q4. Can spread syntax copy an array?
Answer: Yes. For example, const copy = [...original] creates a new array containing the original array's elements. The copy is shallow.
Q5. What is a shallow copy?
Answer: A shallow copy creates a new outer array or object, but nested objects and arrays may still be shared references.
Q6. Why is spread syntax useful in React Native?
Answer: It helps create immutable state updates, merge data, add items to arrays, update object properties, and pass multiple props to components.
Q7. What are template literals?
Answer: Template literals are strings enclosed in backticks. They support expression interpolation with ${...} and allow multiline strings.
Q8. How do you insert a variable into a template literal?
Answer: Use the variable inside ${...}, such as `Hello, ${name}!`.
Q9. What is a JavaScript module?
Answer: A module is a file that can export functionality and import functionality from other files, helping organize and reuse code.
Q10. What is the difference between named and default exports?
Answer: A module can have multiple named exports, imported by name, and one default export, imported using a chosen local name.
Q11. How do you import a named export?
Answer: Use curly braces, for example, import { add } from "./mathUtils".
Q12. How do you import a default export?
Answer: Import it without curly braces, for example, import WelcomeCard from "./WelcomeCard".
Q13. Why should React state not be mutated directly?
Answer: React relies on state updates to determine when to render components. Creating new arrays or objects helps preserve predictable updates and avoids unintended side effects.
Q14. Can a function have more than one rest parameter?
Answer: No. A function definition can contain only one rest parameter, and it must be the final parameter.
Q15. What is the purpose of a utility module?
Answer: A utility module groups reusable functions, such as formatting, validation, calculations, and data transformation, so multiple parts of an application can use them.
17. Quick Revision Checklist
- Spread syntax expands elements or object properties.
- Rest syntax collects remaining values.
- Array spread can copy and combine arrays.
- Object spread can copy and merge object properties.
- Spread copies are shallow unless nested structures are copied separately.
- Rest parameters collect function arguments into an array.
- Template literals use backticks.
- Template literal interpolation uses
${...}.
- JavaScript modules support code reuse through imports and exports.
- Named imports use curly braces and match exported names unless aliased.
- Default imports do not use curly braces.
- React Native state updates should create new arrays or objects instead of mutating existing state.
- Reusable modules make applications easier to maintain and test.
18. Additional Learning Resources
19. Conclusion
ES6+ features make JavaScript development more expressive and maintainable. The spread operator expands arrays and object properties, while rest syntax collects remaining values. Template literals simplify dynamic and multiline strings, and modules help divide an application into reusable files.
In React Native, these features are especially valuable when building reusable components, managing props, updating state immutably, formatting dynamic text, and separating application logic into screens, utilities, and services. Regular practice with these concepts builds a strong foundation for developing modern React Native applications.
Continue Learning: Explore the React Native Training Course and register for a course demo to continue developing your mobile application skills.