Vanilla + ES6 Flashcards
Explain the difference between let, const, and var.
Scoping rules: variables declared by ‘var’ keyword is scoped to immediate function body (function scope) while ‘let’ & ‘const’ variables are scored to the immediate enclosing block (block scope)
Hoisting: variables declared with ‘var’ keyword are hoisted (initialized with undefined before the code runs) which means they are accessible in their enclosing scope even before they are declared. ‘let’ variables are not initialized until their definition is evaluated. Accessing them before the initialization results in a ‘ReferenceError’. The variable is said to be in a “temporal dead zone” from the start of the block until the initialization is processed.
Re-declaration: in strict mode, var will let you re-declare the same variable in the same scope while let raises a SyntaxError.
Global object property: At the top level, let unlike var does not create a property on the global (window) object
What are arrow functions in ES6? How do they differ from traditional function expressions?
New ES6 syntax for writing JS functions; simplies function scope.
Key Difference
Syntax:
- Arrow functions shorter syntax
- simplifies the function and return keywork
this
keyword:
- this
represents the object that called the function
- traditional functions: this gets rescoped to wherever it’s called so it could be the window, document, button or whatever.
- arrow functions: do not have their own this, they inherit it from the parent scope.
Argument object:
- Traditional functions have arguments
object which contains all the args passed to the function. Arrow does not but we can still use Rest parameters (…) to achieve a similar outcome.
Constructors:
- Traditional functions created with function declarations or expressions can be used as constructors (i.e., they can be instantiated with the new keyword). Arrow functions, however, cannot be used as constructors, and will throw an error when used with new.
Implicit Return:
- If the function body of an arrow function is written in one line and does not include curly braces, the value is implicitly returned without needing the return keyword.
Method properties:
- Arrow functions are not suitable to be used as object methods, as this will not be bound to the object. Traditional functions are suitable for this use case.
Can you explain what destructuring is in ES6 and provide an example of how it can be used?
Provides a simple way to unpack values from arrays, or properties from objects, into distinct variables. Also works in function parameters.
Describe how this
keyword works in JavaScript. How does it behave differently in arrow functions compared to regular functions?
In JS, the this
keywork is a special property that refers to the CONTEXT in which a function is called. It’s pretty tricky because what this
refers to can change depending on how and where it’s used.
Global context: this
refers to the global object (window object)
Function context: inside a regular function (not arrow, object method, or event handler), this
refers to the window or global object if not in strict mode. Otherwise undefined.
Method context: this
refers to the object that the method belongs to.
Event handler context: this
refers to the element that received the event.
Constructor context: this
refers to the newly created object
Manually set context: we can set value of this
using the call
, apply
or bind
methods.
Arrow function context: they don’t have their own this
so they inherit it from the parent scope => more predictable => easier to use for callbacks and event handlers.
What is a Promise in JavaScript? Can you describe a scenario where it would be useful?
An object representing the eventual completion or failure of an asynchronous operation. It’s basically a proxy for a value not necessarily known when the promise is created. It’s one way to deal with async operations without blocking the rest of your program.
A promise is in one of these states:
1. Pending: outcome not yet determined
2. Fulfilled: completed, the promise result is a value
3. Rejected: failed, result is an error
Promises can be chained together to perform a sequence of asynchronous operations one after the other. Once a Promise is fulfilled or rejected, it is considered settled and its state cannot change.
Scenario: Fetch API
One common scenario where Promises are useful is when dealing with AJAX requests in a web app. You might want to fetch data from a server, and because network operations can take a significant amount of time, you want them to be asynchronous so they don’t block the rest of your code.
We can then use then
on the Promise object with a callback that takes the response object and return another promise that resolves to data we want to use or perform an operation straight away. catch
to handle errors.
Can you explain how map(), reduce(), and filter() methods work in JavaScript? When would you use each?
A higher order function is a function that takes one or more functions as arguments, or returns a function as its result.
map(), reduce(), and filter() are higher-order functions in JavaScript that operate on arrays. Each takes a callback function as an argument and applies it in a certain way to produce a new result.
map(): creates a new array with the results of calling a provided function on every element in the calling array. It doesn’t modify the original array and returns a new array where the length matches the original assuming there are no undefined.
reduce(): applies a function against an accumulator and each element in the array from left -> right to reduce it to a single output value.
filter(): creates a new array with all the elements that pass the test/condition implemented by the provided function.
When to use each?
map(): when we want to transform all elements in an array
reduce(): when you want to compute a single value from all elements in array
filter(): when you want to select a subset of elements from an array based on a condition
They follow functional programming principles which avoids side effects, mutating data, etc.
What is the difference between a shallow copy and a deep copy? How would you create a deep copy of an object in JavaScript?
A shallow copy of an object is a copy where the new object has its own copy of the primitive data types (like numbers, strings, booleans), but any object references (including arrays, other objects, functions, etc.) point to the same objects as the original.
A deep copy of an object means creating a new object who’s primitive types and object references are copied over, so changes to one object do not affect the other. This includes recursively copying all objects and arrays.
One common method to create a deep copy of an object is to use JSON.stringify() to convert the object into a string, then JSON.parse() to parse it back into a new object:
Explain the concept of closure in JavaScript. Can you provide a practical use case?
A function that has access to its own scope, the outer scope and global scope. Basically, a function that is bundled with its lexical (parent) scope.
Even after outerFunction has finished executing and its execution context has been removed from the stack, innerFunction still has access to outerVariable. This is the power of closures - they keep the scope alive.
They’re particularly useful when you want to create functions on the fly with data that they should “remember”, or when you’re doing data encapsulation or object data privacy.
A practical use case for closures is data privacy or creating “private” variables. In JavaScript, we can’t have private variables due to its scoping rules, but closures give us a way to mimic them.
What are Template Literals in ES6? Can you give an example of how they can be used to enhance string manipulation?
A new way to define strings in JS. They provide a concise and convenient syntax to create strings and allow us to embed expressions within strings, perform multi-line string formatting, and create tagged templates.
What is the spread operator and how is it used in JavaScript? Can you provide an example?
The spread (…) operator in JS is used to unpack elements of iterable objects (like arrays or objects) into a list of elements.
Use cases:
1. Copying arrays: shallow copy of an array
2. Concatenating arrays let arr3 = [...arr1, ...arr2]
3. Using in function arguments sum(a,b,c) => sum(…[1,2,3])
4. Spreading objects: copy properties from one object to another
How does JavaScript handle asynchronous operations? Can you explain the event loop and how it relates to the call stack?
JS is single-threaded, so it executes one operation at a time in a single sequence. However, many operations like network requests are asynchronous, and can take a significant amount of time to complete. If a program waits for these operations synchronously, it would block the rest of the program from running.
To handle this, JS uses a combination of the CALL STACK, BROWSER WEB APIs, EVENT QUEUE, and the EVENT LOOP.
Call stack: a DS that tracks the program execution context to know what function is currently running and its nested calls. JS can only do one thing at a time since it has only one call stack. If a function is called, it’s added to the top of the stack. If a function is complete, it’s removed from the stack.
Web APIs and Event Queue: When an async operation like setTimeout or AJAX call is encountered. The operation is sent to be handled by the browser. Once the operation finishes (like timer expires, the network request receives a response), the provided callback function is sent to the event queue.
Event loop: this continuously checks if the call stack is empty. If yes: take the first callback function in the queue and pushes it onto the call stack to be executed.
In short: call stack handles the synchronous, line-by-line execution of code. Web APIs handle async operations. Once they are done, the callbacks enters the event queue. The event loop monitors the call stack and event queue, moving callbacks onto the stack when the stack is empty.
These all allow JS to be non-blocking and async, so it can finish other tasks without waiting for previous time consuming ones => performance and UX.
What is the purpose of async/await introduced in ES8? How does it improve upon promises?
Async/Await basically provides a more concise and easier-to-understand syntax for working with Promises, which are used for asynchronous operations in JavaScript.
Promises significantly improved the way we handle asynchronous operations, helping us avoid “callback hell” and making the code more readable and maintainable. However, Promises still use callbacks (.then() and .catch()), and complex Promise chains can still lead to somewhat difficult-to-read code.
So this is where async/await comes in:
Async: When placed before a function, async ensures that the function returns a Promise. If a value is returned from the function, it will be wrapped in a resolved Promise. If nothing is returned, a Promise that resolves to undefined is returned. If an error is thrown, it will return a rejected Promise.
Await: the await operator is used only inside an async function and makes JS wait until the Promise returns a result. It essentially pauses the execution of the async function and resumes when the Promise is either resolved or rejected, and then returns the result. This allows us to write asynchronous code that appears synchronous. Making it easier to read and understand. Also easier with try/catch block syntactic sugar for error handling which isn’t possible with plain promises.
Explain what ‘hoisting’ means in JavaScript. How does this behavior differ for var, let, and const?
Hoisting is a JavaScript mechanism where variable and function declarations are put into memory during the compile phase. This means that no matter where functions and variables are declared, they are moved to the top of their scope regardless of whether their scope is a global or a local.
var: when you declare a variable with ‘var’, both the declaration and the definition are hoisted (but definition is hoisted as ‘undefined’.
let and const: only the declarations are hoisted to the top of the block so you will get ‘ReferenceError’ because they are in the temporal dead zone from the start of the block until the declaration is processed.
Also this is likely because of var being in global scope as well since it’s is also added to the Window/Global object.
What are JavaScript modules in ES6? How do they differ from libraries in ‘vanilla’ JavaScript?
ES6 introduced the concept of JavaScript modules, which allow developers to break up their code into separate, reusable pieces, each encapsulating the code and functionality that serves a single purpose.
Modules are defined using export and imported using import. They are different from scripts because they allow you to export and import functions, objects, or values from one module to another, facilitating the reusability and maintainability of code.
Explain the Prototype Chain and Inheritance in JavaScript.
Every object in JavaScript has a built-in property, which is called its prototype. The prototype is itself an object, so the prototype will have its own prototype, making what’s called a prototype chain. The chain ends when we reach a prototype that has null
for its own prototype.
Prototype
> It’s like a map for that object type, it contains the functionalities for that object ttype. Any object that is instantiated will point to that prototype instead of creating new property in every instance (like a singleton in some way).
In a lot of cases it’s better to use prototype properties to add methods rather than creating the method inside the class.
JavaScript uses a Prototype Inheritance Model. That means that every constructor function/class has a prototype property that is shared by every instance of the constructor/class. So any methods and properties or prototype can be accessed by every instance. Prototype property returns an object.
Every object type has a prototype, and all instances created from that object type inherit the prototype methods. Only the constructor function has the prototype property.
function User(name) {
this.name = name;
}
User.prototype.sayHi = function () {
console.log(this.name);
};
let user = new User(‘John’);
user.sayHi(); // John
Differences between == and === operators.
Triple Equals (===)
- Returns true if both operands are of the same type and contain the same value.
- Does not perform type conversion.
- Preferred operator for equality comparison in JavaScript.
- Use it when you want to compare values strictly, considering both value and type.
Double Equals (==)
- Tries to perform type conversion if the types of the operands differ and then compares for equality.
- Performs complex type conversions, which can lead to unexpected results.
- Not recommended for equality comparison in JavaScript.
- Use it cautiously and with awareness of the type conversion rules.