Normal Function Superpower: Normal functions get their ownthis context, which changes based on how they're called. (Explain call, apply, bind, and method calls).
// Code example: `this` in a normal function method const person = { name: "Alice", greet: function() { console.log(`Hello, my name is ${this.name}`); } }; person.greet(); // Output: "Hello, my name is Alice"
// Code example: `this` with call/apply/bind const anotherPerson = { name: "Bob" }; person.greet.call(anotherPerson); // Output: "Hello, my name is Bob"
Arrow Function’s Limitation: Arrow functions don’t have their own this. They lexically inherit this from their surrounding scope. Explain why this is often a feature but can be a limitation when you need dynamic this.
// Code example: `this` in an arrow function const anotherPerson = { name: "Charlie", greet: () => { // 'this' here refers to the global object (or 'undefined' in strict mode) // NOT the 'anotherPerson' object console.log(`Hello, my name is ${this.name}`); } }; anotherPerson.greet(); // Output might be "Hello, my name is undefined" or similar, depending on environment
// When lexical 'this' is useful (e.g., inside a method) const counter = { value: 0, start: function() { // Normal function for the method itself to get 'this' of 'counter' setInterval(() => { // Arrow function here inherits 'this' from 'start' method this.value++; console.log(this.value); }, 1000); } }; counter.start();
Hoisting Heroics: When You Can Call Before You Declare
Normal Function Superpower: Function declarations (a type of normal function) are hoisted, meaning you can call them before they appear in the code.
// Code example: Hoisting a normal function declaration sayHello(); // Output: "Hello from a hoisted function!"
function sayHello() { console.log("Hello from a hoisted function!"); }
Arrow Function’s Limitation: Arrow function expressions (and function expressions in general) are not hoisted in the same way. You need to declare them before you use them.
// Code example: No hoisting for arrow functions // sayGoodbye(); // This would throw a ReferenceError
const sayGoodbye = () => { console.log("Goodbye from an arrow function!"); }; sayGoodbye(); // This works
The arguments Arsenal: Accessing All Inputs
Normal Function Superpower: Normal functions have access to the arguments object, a built-in array-like structure containing all the arguments passed to the function.
// Code example: `arguments` object in a normal function function sumAllNormal() { let sum = 0; for (let i = 0; i < arguments.length; i++) { sum += arguments[i]; } console.log("Normal function sum:", sum); } sumAllNormal(1, 2, 3, 4); // Output: "Normal function sum: 10"
Arrow Function’s Limitation: Arrow functions do not have their own arguments object. You'd typically use rest parameters (...args) instead.
// Code example: No `arguments` object in arrow functions const sumAllArrow = (...args) => { // Use rest parameters instead const sum = args.reduce((acc, current) => acc + current, 0); console.log("Arrow function sum (with rest parameters):", sum); // console.log(arguments); // This would throw a ReferenceError or refer to outer scope's arguments }; sumAllArrow(1, 2, 3, 4); // Output: "Arrow function sum (with rest parameters): 10"
Constructor Caper: Building New Objects with new
Normal Function Superpower: Normal functions can be used as constructors with the new keyword to create new instances of objects.
// Code example: Normal function as a constructor function Car(make, model) { this.make = make; this.model = model; } const myCar = new Car("Honda", "Civic"); console.log(myCar); // Output: Car { make: "Honda", model: "Civic" }
Arrow Function’s Limitation: Arrow functions cannot be used as constructors. Trying to use new with an arrow function will throw an error.
// Code example: Arrow function cannot be a constructor const Boat = (make, model) => { this.make = make; this.model = model; }; // const myBoat = new Boat("Yamaha", "Speedster"); // This would throw TypeError: Boat is not a constructor // console.log(myBoat);
Duplicate Debacle: Parameter Names
Normal Function Superpower: In non-strict mode, normal functions allow duplicate parameter names (though this is generally bad practice).
// Code example: Duplicate parameters in normal function (non-strict mode) // Note: This is generally bad practice and should be avoided in new code. function oldSchoolParams(a, b, a) { console.log("Normal function with duplicate params:", a, b); } oldSchoolParams(1, 2, 3); // Output: "Normal function with duplicate params: 3 2" (last 'a' wins)
Arrow Function’s Limitation: Arrow functions, by their nature, are always in strict mode regarding parameters, meaning duplicate parameter names will throw a SyntaxError.
// Code example: Duplicate parameters throw error in arrow functions // const modernParams = (x, y, x) => { // This line would cause a SyntaxError during parsing // console.log(x, y); // }; // modernParams(10, 20, 30);
Generator Glimmer: Pausing and Resuming Execution
Normal Function Superpower: Normal functions can be designated as generator functions (function*) allowing for iterable, pauseable execution.
// Code example: Normal function as a generator function* idGenerator() { let id = 1; while (true) { yield id++; } } const gen = idGenerator(); console.log("Generated ID 1:", gen.next().value); // Output: "Generated ID 1: 1" console.log("Generated ID 2:", gen.next().value); // Output: "Generated ID 2: 2"
Arrow Function’s Limitation: Arrow functions cannot be generator functions.
// Code example: Arrow function cannot be a generator // const arrowGenerator =* () => { // SyntaxError: * is not allowed in arrow function parameters // yield 1; // };
Event Listener Elixir: this Context in DOM Events
Normal Function Superpower: When a normal function is used as an event listener, its this context refers to the element that triggered the event.
// Code example: `this` in a normal event listener // (Imagine this running in a browser console) /* <button id="myButton">Click Me</button> <script> document.getElementById('myButton').addEventListener('click', function() { console.log(this); // 'this' refers to the button element itself this.textContent = "Clicked!"; }); </script> */
Arrow Function’s Limitation: If an arrow function is used as an event listener, its this will lexically inherit from its surrounding scope, which might not be the element you expect. This is a common gotcha!
// Code example: `this` in an arrow event listener // (Imagine this running in a browser console) /* <button id="myOtherButton">Click Me Too</button> <script> // Outside any specific function scope, 'this' would be global (window) // if this code block isn't wrapped in an outer function. // Let's assume it's in a global script. document.getElementById('myOtherButton').addEventListener('click', () => { console.log(this); // 'this' refers to the 'window' object (or 'undefined' in strict modules) // NOT the button element }); </script> */