ES6+ JavaScript Features
Arrow Functions
// Traditional function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => a + b;
// Single parameter (no parentheses needed)
const double = x => x * 2;
// Multiple statements need curly braces and return
const calculate = (price, qty) => {
const subtotal = price * qty;
const tax = subtotal * 0.1;
return subtotal + tax;
};
Destructuring
// Object destructuring
const product = {
name: 'Widget Pro',
price: 29.99,
sku: 'WDG-001',
stock: 150
};
const { name, price, stock } = product;
console.log(name); // 'Widget Pro'
console.log(price); // 29.99
// Renaming variables
const { name: productName, price: productPrice } = product;
// Default values
const { name, weight = 0.5 } = product; // weight defaults to 0.5
// Array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
console.log(first); // 'red'
// Skip elements
const [, , last] = colors;
console.log(last); // 'blue'
// Function parameter destructuring
function displayProduct({ name, price }) {
console.log(`${name}: $${price}`);
}
displayProduct(product); // 'Widget Pro: $29.99'
Template Literals
const name = 'Widget Pro';
const price = 29.99;
// String interpolation
const message = `${name} costs $${price.toFixed(2)}`;
// Multi-line strings
const html = `
<div class="product">
<h2>${name}</h2>
<p class="price">$${price}</p>
</div>
`;
// Expression interpolation
const discount = 10;
const finalPrice = `${name}: $${(price * (1 - discount/100)).toFixed(2)}`;
Spread and Rest Operators
// Spread operator (...)
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5]; // [1, 2, 3, 4, 5]
const obj1 = { a: 1, b: 2 };
const obj2 = { ...obj1, c: 3 }; // { a: 1, b: 2, c: 3 }
// Merge objects (later properties override earlier)
const defaults = { color: 'red', size: 'medium' };
const custom = { color: 'blue' };
const result = { ...defaults, ...custom }; // { color: 'blue', size: 'medium' }
// Rest parameters
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
sum(1, 2, 3, 4); // 10
Optional Chaining and Nullish Coalescing
// Optional chaining (?.)
const user = {
name: 'John',
address: {
city: 'New York'
}
};
const city = user?.address?.city; // 'New York'
const zip = user?.address?.zip; // undefined (no error)
const phone = user?.contact?.phone; // undefined (no error)
// Nullish coalescing (??)
const name = user?.name ?? 'Anonymous'; // 'John'
const age = user?.age ?? 0; // 0 (null/undefined only)
// vs logical OR (||)
const value = 0 || 'default'; // 'default' (0 is falsy)
const value2 = 0 ?? 'default'; // 0 (0 is not null/undefined)
Array Methods
const products = [
{ id: 1, name: 'Widget', price: 29.99, active: true },
{ id: 2, name: 'Gadget', price: 49.99, active: true },
{ id: 3, name: 'Doohickey', price: 19.99, active: false },
{ id: 4, name: 'Thingamajig', price: 39.99, active: true }
];
// map - transform each element
const names = products.map(p => p.name);
// ['Widget', 'Gadget', 'Doohickey', 'Thingamajig']
// filter - keep elements that pass test
const activeProducts = products.filter(p => p.active);
// [{id: 1, ...}, {id: 2, ...}, {id: 4, ...}]
// find - get first matching element
const gadget = products.find(p => p.name === 'Gadget');
// {id: 2, name: 'Gadget', ...}
// reduce - accumulate to single value
const totalPrice = products
.filter(p => p.active)
.reduce((sum, p) => sum + p.price, 0);
// 119.97
// some / every
const hasExpensive = products.some(p => p.price > 40); // true
const allActive = products.every(p => p.active); // false
DOM Manipulation and Events
Selecting Elements
// Modern selectors (preferred)
const element = document.querySelector('.product-name'); // First match
const elements = document.querySelectorAll('.product-item'); // All matches
// Get by ID (returns single element)
const header = document.getElementById('header');
// Navigate the DOM
const parent = element.parentElement;
const children = element.children;
const next = element.nextElementSibling;
const previous = element.previousElementSibling;
Modifying Elements
// Text content
element.textContent = 'New text'; // Plain text
element.innerHTML = '<b>Bold text</b>'; // HTML (be careful with XSS)
// Attributes
element.setAttribute('data-product-id', '123');
element.getAttribute('data-product-id');
element.removeAttribute('disabled');
element.hasAttribute('required');
// Classes
element.classList.add('active');
element.classList.remove('hidden');
element.classList.toggle('visible');
element.classList.contains('active'); // true/false
// Styles
element.style.color = 'red';
element.style.backgroundColor = '#fff';
element.style.display = 'none';
element.style.cssText = 'color: red; font-size: 16px;';
// Create and insert elements
const newDiv = document.createElement('div');
newDiv.classList.add('product-card');
newDiv.innerHTML = '<h3>New Product</h3>';
document.querySelector('.product-grid').appendChild(newDiv);
// Insert before existing element
const reference = document.querySelector('.product-item');
reference.parentNode.insertBefore(newDiv, reference);
Event Handling
// Add event listener
const button = document.querySelector('.add-to-cart');
button.addEventListener('click', function(event) {
event.preventDefault();
const productId = this.dataset.productId;
addToCart(productId);
});
// Event object
button.addEventListener('click', (event) => {
event.target; // Element that triggered the event
event.currentTarget; // Element with the listener
event.type; // 'click'
event.preventDefault(); // Prevent default action
event.stopPropagation(); // Stop event bubbling
});
// Delegation (for dynamically added elements)
document.querySelector('.product-grid').addEventListener('click', (event) => {
const productItem = event.target.closest('.product-item');
if (!productItem) return;
const productId = productItem.dataset.productId;
console.log('Clicked product:', productId);
});
Common DOM Patterns
// Show/hide elements
toggleElement(selector) {
document.querySelector(selector).classList.toggle('hidden');
}
// Form validation
const form = document.querySelector('#checkout-form');
form.addEventListener('submit', (event) => {
const email = form.querySelector('#email');
const error = form.querySelector('#email-error');
if (!email.value.includes('@')) {
event.preventDefault();
error.textContent = 'Please enter a valid email';
email.focus();
}
});
// Infinite scroll / lazy loading
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadMoreProducts();
observer.unobserve(entry.target);
}
});
});
observer.observe(document.querySelector('.load-more-trigger'));
Key Takeaway
Use querySelector/querySelectorAll for element selection, addEventListener for events, and event delegation for dynamic content. Always prefer classList methods over direct className assignment.
Fetch API, Async/Await, and Magento JavaScript
Fetch API
// GET request
async function getProducts() {
const response = await fetch('/rest/V1/products', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiToken}`
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
}
// POST request
async function createProduct(productData) {
const response = await fetch('/rest/V1/products', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiToken}`
},
body: JSON.stringify({
product: productData
})
});
return await response.json();
}
// Error handling
async function safeFetch(url, options = {}) {
try {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || `HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Fetch error:', error);
throw error;
}
}
Async/Await
// Async functions always return a Promise
async function loadProduct(productId) {
// Await pauses execution until promise resolves
const product = await fetch(`/rest/V1/products/${productId}`)
.then(r => r.json());
const reviews = await fetch(`/rest/V1/products/${productId}/reviews`)
.then(r => r.json());
return { product, reviews };
}
// Parallel requests
async function loadDashboard() {
const [products, orders, customers] = await Promise.all([
fetch('/api/products').then(r => r.json()),
fetch('/api/orders').then(r => r.json()),
fetch('/api/customers').then(r => r.json())
]);
return { products, orders, customers };
}
// Sequential with error handling
async function processOrder(orderId) {
try {
const order = await getOrder(orderId);
const payment = await processPayment(order);
await sendConfirmation(order, payment);
return { success: true, orderId };
} catch (error) {
console.error('Order processing failed:', error);
await notifyAdmin(error);
return { success: false, error: error.message };
}
}
Magento JavaScript (RequireJS)
Magento uses RequireJS for module loading:
// RequireJS module definition
define([
'jquery',
'Magento_Ui/js/modal/modal'
], function ($, modal) {
'use strict';
return function (config) {
const options = {
type: 'popup',
responsive: true,
innerScroll: true,
title: config.title,
buttons: [{
text: $.mage.__('Close'),
class: 'action primary close-modal',
click: function () {
this.closeModal();
}
}]
};
$(config.modalSelector).modal(options);
};
});
// Usage in PHTML
// <script type="text/x-magento-init">
// {
// "*": {
// "Magento_Ui/js/core/app": {
// "component": "Vendor_Module/js/product-modal"
// }
// }
// }
// </script>
Magento Custom JS Component
// web/js/product-list.js
define([
'jquery',
'uiComponent',
'mage/url'
], function ($, Component, urlBuilder) {
'use strict';
return Component.extend({
defaults: {
template: 'Vendor_Module/product-list',
products: []
},
initialize: function () {
this._super();
this.loadProducts();
return this;
},
loadProducts: function () {
const apiUrl = urlBuilder.build('rest/V1/products');
$.ajax({
url: apiUrl,
type: 'GET',
dataType: 'json',
success: function (data) {
this.products(data.items);
}.bind(this)
});
},
getProducts: function () {
return this.products();
}
});
});
Key Takeaway
Modern JavaScript uses fetch + async/await for HTTP requests. Magento uses RequireJS for module loading and KnockoutJS for UI components. Always handle errors in async code.
Quiz
1. What is the output of: const [a, b] = [1, 2, 3]?
2. What does the ?. operator do in JavaScript?
3. What does async/await do?
4. What module loader does Magento use?
Flashcards
Question
What is destructuring in JavaScript?
Click to reveal answer
Answer
Syntax for extracting values from arrays or objects into variables. const { name, price } = product; or const [first, second] = array;
Question
What does the spread operator (...) do?
Click to reveal answer
Answer
Expands an iterable into individual elements. Used for: copying arrays [...arr], merging objects {...obj}, and passing arguments (...args).
Question
What is the difference between null and undefined?
Click to reveal answer
Answer
undefined: variable declared but not assigned. null: intentionally empty value. Use ?? (nullish coalescing) to check for both.
Question
How do you handle errors in async/await?
Click to reveal answer
Answer
Use try/catch blocks. async function foo() { try { await bar(); } catch (error) { console.error(error); } }
Question
What is event delegation?
Click to reveal answer
Answer
Attaching a single event listener to a parent element instead of individual children. Works for dynamically added elements. Use event.target.closest() to find the actual target.
Question
How does Magento load JavaScript modules?
Click to reveal answer
Answer
Using RequireJS. define() defines a module, require() loads it. Magento PHTML templates use data-magento-init script tags to initialize components.
Question
What does the fetch API return?
Click to reveal answer
Answer
A Promise that resolves to a Response object. Use .json() to parse JSON body. Check response.ok for success. Example: const data = await fetch(url).then(r => r.json());
Question
What is template literal syntax?
Click to reveal answer
Answer
Backticks (`) with ${expression} interpolation. Supports multi-line strings. Example: `Hello ${name}, you have ${count} items`.
Revision Notes
Key Takeaways
- 1. ES6+ features: arrow functions, destructuring, template literals, spread operator, optional chaining
- 2. DOM manipulation: querySelector, addEventListener, classList, dataset
- 3. Fetch API + async/await for HTTP requests in modern JavaScript
- 4. Event delegation for handling dynamically added elements
- 5. Magento uses RequireJS for module loading and KnockoutJS for UI components
- 6. Always handle errors with try/catch in async functions
- 7. Optional chaining (?.) prevents null/undefined reference errors
Interview Tips
- • Explain the difference between var, let, and const
- • Describe how event delegation works and when to use it
- • Know the fetch API and how to handle errors with async/await
- • Understand how Magento loads JavaScript (RequireJS, data-magento-init)
- • Be able to explain Promises and how async/await simplifies them
Cheat Sheet
JavaScript ES6+ Cheat Sheet
Destructuring:
const { name, price } = product;
const [first, second] = array;
Arrow Functions:
const add = (a, b) => a + b;
const greet = name => `Hello ${name}`;
Template Literals:
`Hello ${name}, you have ${count} items`
Optional Chaining:
user?.address?.city // undefined if any null
value ?? 'default' // null/undefined only
Fetch API:
const data = await fetch(url).then(r => r.json());
Array Methods:
arr.map(x => x * 2) // Transform
arr.filter(x => x > 0) // Keep matching
arr.find(x => x.id === 1) // Find first
arr.reduce((sum, x) => sum + x, 0) // Accumulate
Magento RequireJS:
define(['jquery'], function($) {
return function(config) { /* ... */ };
});