Javascript Flashcards
Learn Javascript basics and advance concepts
What is the nullish coalescing operator
in JavaScript?
Nullish coalescing
is a JavaScript logical operator represented by two question marks (??
). Nullish coalescing is an operator that returns the first “defined
” value. “defined
” here refers to an expression whose value is neither null
nor undefined
.
What is a spread operator
?
Spread operator
allows iterables such as arrays
, objects
, and strings
to be expanded into single arguments. The spread operator is denoted by three dots (...
) followed by the variable to be expanded.
Explain the “this
” keyword.
To access the object, a method can use the this
keyword.
In JavaScript, this
keyword refers to the object
it belongs to. Depending on the context, this might have several meanings. This pertains to the global object in a function and the owner object in a method, respectively.
What is Function Binding
? What are the differences between call
, apply
, and bind
?
Function binding
:
Function binding
refers to the process of associating a function with a specific context (this
value). The bind()
method creates a new function that, when called, has its this
keyword set to the provided value.
const person = { name: 'GFG', greet: function() { console.log('Hello, ' + this.name); } }; const greet = person.greet; greet(); output: Hello, undefined
When greet is called directly (without being bound to the person
object), the value of this
is not person
anymore. Instead, it refers to the global object (in non-strict mode) or is undefined (in strict mode).
call
The call()
method immediately invokes a function, allowing you to set the value of this and pass arguments to the function.
const person = { name: 'GFG', greet: function(city) { console.log('Hello, ' + this.name + ' from ' + city); } }; person.greet('Delhi'); const greet = person.greet; greet.call(person, 'Noida'); output: Hello, GFG from Delhi Hello, GFG from Noida
This code defines a greet method in the person object, calls it with ‘Delhi
’ directly, and then uses call()
to invoke the method with ‘Noida
’, ensuring the this context is correctly bound to person.
apply
Similar to call()
, the apply()
method invokes a function and allows you to set the value of this, but the difference is that the arguments are passed as an array
(or an array-like object
).
const person = { name: 'GFG', greet: function(city, country) { console.log('Hello, ' + this.name + ' from ' + city + ', ' + country); } }; person.greet('Delhi', 'India'); const greet = person.greet; greet.apply(person, ['Noida', 'Delhi']); output: Hello, GFG from Delhi, India Hello, GFG from Noida, Delhi
This code defines a greet()
method in the person object, calls it with ‘Delhi
’ and ‘India
’, then uses apply()
to invoke the method with ‘Noida
’ and ‘Delhi
’ by passing arguments as an array while maintaining the this context.
bind
The bind()
method is used to create a new function that, when called, has its this value set to a specified value, regardless of how the function is invoked.
const person = { name: 'GFG', greet: function() { console.log('Hello, ' + this.name); } }; const greet = person.greet; const boundGreet = greet.bind(person); boundGreet(); output: Hello, GFG
This code defines a person
object with a greet method, binds it to the person object using bind()
, and then calls the bound function to correctly reference the person’s name when executed.
What are anonymous functions
in JavaScript?
An anonymous function
is a function that does not have any name associated with it. We usually use the function keyword followed by the function’s name to define a function in JavaScript. Anonymous functions
omit the function name, making it not accessible after its creation.
An anonymous function
can only be accessed by a variable. The anonymous nature of the function makes it great for passing in functions as arguments to other functions (higher-order functions
) and functions that are invoked immediately upon initialization.
What is hoisting
in JavaScript?
Hoisting
refers to the process where the interpreter moves the declaration of classes
, functions
, or variables
to the top of their scope, before their execution.
Hoisting allows developers to reference variables
, functions
, and classes
before they are declared.
Referencing a variable before its initialization would return the variable’s default value (undefined
for variables declared using the var
keyword).
console.log(foo); var foo = 'foo';
It might surprise you that this code outputs undefined
and doesn’t fail or throw an error – even though foo gets assigned after we console.log
it!
This is because the JavaScript interpreter splits the declaration and assignment of functions and variables: it “hoists
” your declarations to the top of their containing scope before execution.
This process is called hoisting
, and it allows us to use foo
before its declaration in our example above.
What is a callback
function in JavaScript?
A callback function
is a function passed into another function as an argument. The callback function is then invoked inside the callee to complete an action.
What are the different states of a Promise
?
-
Pending
– Promise’s initial state, waiting for the operation to complete -
Fulfilled
– Promise’s operation was completed successfully -
Rejected
– Promise’s operation failed -
Settled
– Promise is either fulfilled or rejected
How to handle errors
using Promises?
Promise chains
are great at error handling
. When a promise rejects
, the control jumps to the closest rejection handler. That’s very convenient in practice.
The .catch
doesn’t have to be immediate. It may appear after one or maybe several .then
.
Normally, such .catch
doesn’t trigger at all. But if any of the promises above rejects (a network problem or invalid json or whatever), then it would catch it.
The code of a promise executor and promise handlers has an “invisible try..catch
” around it. If an exception happens, it gets caught and treated as a rejection.
The “invisible try..catch
” around the executor automatically catches the error and turns it into rejected promise.
This happens not only in the executor function, but in its handlers as well. If we throw inside a .then
handler, that means a rejected promise, so the control jumps to the nearest error handler.
new Promise((resolve, reject) => { resolve("ok"); }).then((result) => { throw new Error("Whoops!"); // rejects the promise }).catch(alert); // Error: Whoops!
What is Promise.all
?
Promise.all
is a type of Promise that accepts an array of Promises and waits for each Promise to resolve
. Promise.all resolves once each of the Promise inputs resolves, emitting an array of results in the then block. A rejected Promise in the input will cause the Promise.all Promise to also get rejected
.
Explain async/await
in JavaScript.
The async
keyword is placed before a function to denote that the function is asynchronous
and can be used as a Promise
.
The await
keyword, on the other hand, tells JavaScript to wait for the async operation to complete before proceeding to the next task in the function. The await
keyword can only be used in an async function.
What are rest
parameters?
The rest parameter
syntax allows a function to accept an indefinite number of arguments as an array. The rest operator
puts the contents of the variable after the rest operator into an array (rest parameter
can only be used as the last parameter of a function).
Rest operator
is represented by three dots (...
) followed by the variable name. The variable can then be used to access the array containing the contents of the function’s arguments.
What is a WeakMap
and WeakSet
in JavaScript?
WeakMap
:
The first difference between Map
and WeakMap
is that keys must be objects, not primitive values.
If we use an object as the key in it, and there are no other references to that object – it will be removed from memory (and from the map) automatically.
let john = { name: "John" }; let weakMap = new WeakMap(); weakMap.set(john, "..."); john = null; // overwrite the reference // john is removed from memory!
let john = { name: "John" }; let map = new Map(); map.set(john, "..."); john = null; // overwrite the reference // john is stored inside the map, // we can get it by using map.keys()
Compare it with the regular Map
example above. Now if john
only exists as the key of WeakMap
– it will be automatically deleted from the map
(and memory).
WeakMap
does not support iteration and methods keys()
, values()
, entries()
, so there’s no way to get all keys or values from it.
WeakMap has only the following methods:
weakMap.set(key, value)
weakMap.get(key)
weakMap.delete(key)
weakMap.has(key)
Why such a limitation? That’s for technical reasons. If an object has lost all other references (like john
in the code above), then it is to be garbage-collected automatically. But technically it’s not exactly specified when the cleanup happens.
WeakSet
:
WeakSet behaves similarly:
- It is analogous to
Set
, but we may only add objects toWeakSet
(not primitives). - An object exists in the set while it is reachable from somewhere else.
- Like
Set
, it supportsadd
,has
anddelete
, but notsize
,keys()
and no iterations.
let visitedSet = new WeakSet(); let john = { name: "John" }; let pete = { name: "Pete" }; let mary = { name: "Mary" }; visitedSet.add(john); // John visited us visitedSet.add(pete); // Then Pete visitedSet.add(john); // John again // visitedSet has 2 users now // check if John visited? alert(visitedSet.has(john)); // true // check if Mary visited? alert(visitedSet.has(mary)); // false john = null; // visitedSet will be cleaned automatically
The most notable limitation of WeakMap
and WeakSet
is the absence of iterations, and the inability to get all current content. That may appear inconvenient, but does not prevent WeakMap/WeakSet
from doing their main job – be an “additional” storage of data for objects which are stored/managed at another place.
What is typecasting
in JavaScript?
Typecasting
or coercion
means to change the data type of a value to another data type. For example, a conversion from a string to an integer or vice versa.
Coercion
can either be implicit or explicit. Implicit coercion is when the type conversion
is automatic, whereas explicit coercion
is when a developer explicitly writes code to convert the type of a value. The latter is also known as typecasting
.
There are three typecasts provided by JavaScript:
-
Boolean(value)
– Casts the input value to a boolean value -
Number(value)
– Casts the input value to a float or integer value -
String(value)
– Casts the input value to a string
What is event bubbling
in JavaScript?
When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.
<form onclick="alert('form')">FORM <div onclick="alert('div')">DIV <p onclick="alert('p')">P</p> </div> </form>
A click on the inner <p>
first runs onclick:
- On that
<p>
. - Then on the outer
<div>
. - Then on the outer
<form>
. - And so on upwards till the document object.
Event bubbling
is a way of event propagation in the HTML DOM API
, where events are handled from starting from the innermost element propagating outwards to its parent elements.
Let’s look at an example where an event occurs in a nested element where both elements have a registered event handler for the triggered event. Event bubbling
causes the event to be captured and handled by the innermost element first. It is then propagated to the outer elements.
What is event capturing
in JavaScript?
Event capturing
is another way of event propagation in the HTML DOM API
. Unlike event bubbling
, event capturing
propagates an event from the outermost element to the target element.
There’s another phase of event processing called “capturing
”. It is rarely used in real code, but sometimes can be useful.
The standard DOM Events describes 3 phases of event propagation:
-
Capturing phase
– the event goes down to the element. -
Target phase
– the event reached the target element. -
Bubbling phase
– the event bubbles up from the element.
Capturing
phase is rarely used.
In fact, the capturing phase was invisible for us, because handlers added using on<event>
-property or using HTML attributes or using two-argument addEventListener(event, handler)
don’t know anything about capturing, they only run on the 2nd and 3rd phases.
To catch an event on the capturing phase, we need to set the handler capture option to true:
elem.addEventListener(..., {capture: true}) // or, just "true" is an alias to {capture: true} elem.addEventListener(..., true)
Note that while formally there are 3 phases, the 2nd phase (“target phase
”: the event reached the element) is not handled separately: handlers on both capturing
and bubbling
phases trigger at that phase.
<form>FORM <div>DIV <p>P</p> </div> </form> <script> for(let elem of document.querySelectorAll('*')) { elem.addEventListener("click", e => alert(`Capturing: ${elem.tagName}`), true); elem.addEventListener("click", e => alert(`Bubbling: ${elem.tagName}`)); } </script>
The code sets click handlers on every element in the document to see which ones are working.
If you click on <p>
, then the sequence is:
HTML → BODY → FORM → DIV -> P
P → DIV → FORM → BODY → HTML
P
(capturing phase, the first listener):HTML → BODY → FORM → DIV -> P
P → DIV → FORM → BODY → HTML
P
(bubbling phase, the second listener).
Please note, the HTML → BODY → FORM → DIV -> P
P → DIV → FORM → BODY → HTML
P
shows up twice, because we’ve set two listeners: capturing
and bubbling
. The target triggers at the end of the first and at the beginning of the second phase.
What is a closure
in JavaScript?
A closure
is an inner function that has access to the variables in the enclosing/outer function’s scope. The closure
has access to variables from three scopes:
- within its own scope
- within the enclosing function’s scope
- global variables
Closures
are particularly useful on the web because of the web’s event-based nature. A common pattern in front-end JavaScript is as follows: you define a behavior, then attach it to an event that is triggered by user input.
When a function is defined within another function, it retains access to the variables and parameters of the outer function, even after the outer function has finished executing. That link, between the inner function and its scope inside the outer function is known as closure
You can use them to create private variables that can only be accessed by the inner function, you can even use them to create complex objects with access to a rich context that is only available globally to them.
What does the instanceof
operator do?
The instanceof
operator checks whether the prototype property of a constructor appears anywhere in the prototype chain
of an object. In other words, the instanceof
operator checks if the object is an instance of a class or not at run time.
How do you create a shallow copy
of an object?
Deep copying
means that the value of the new variable is disconnected from the original variable while a shallow copy
means that some values are still connected to the original variable.
First of all, a deep copy
is a copy of an object completely disconnected from the original variable. A shallow copy
on the other hand is a copy of the original variable where some values are still connected to the original variable.
There are two main ways to create a shallow copy
of an object in JavaScript:
- Using the
spread operator
- Using
Object.assign()
What is the difference between local storage
and session storage
?
Local storage
is a read-only
property of the window interface to access a storage object. The stored data is saved indefinitely across browser sessions. Data stored in local storage
is only cleared when removed explicitly through the browser’s settings or programmatically by the application.
Session storage
is similar to local storage
with the key difference being the data stored’s expiration time. Data stored in session storage gets cleared when the page session ends. A page session lasts as long as the tab or browser is open and persists between page reloads and restores.
What is the Temporal Dead Zone
in JavaScript?
In ES6, variables declared using let
and const
are hoisted similar to var
, class
and function
. However, there is a period between the variable’s declaration and when it enters scope where the variable can’t be accessed. This period is called the Temporal dead zone (TDZ).
When the interpreter hoists a variable declared with var
, it initializes its value to undefined. The first line of code below will output undefined
:
console.log(foo); // undefined var foo = 'bar'; console.log(foo); // "bar"
Variables declared with let
and const
are hoisted but not initialized with a default value. Accessing a let or const variable before it’s declared will result in a ReferenceError:
{ // Start of foo's TDZ let bar = 'bar'; console.log(bar); // "bar" console.log(foo); // ReferenceError because we're in the TDZ let foo = 'foo'; // End of foo's TDZ }
What are the differences between mutable
and immutable
objects?
Mutable
Objects:
In JavaScript, the following types are mutable: arrays
, objects
, and functions
.
Using the push()
and pop()
methods are mutate
the original array by adding and removing elements.
Immutability
:
JavaScript primitives
such as strings
and numbers
are immutable
. Once created, their values cannot be changed.
Benefits:
-
Mutability
can lead to unexpected side effects and bugs when multiple parts of your code share and modify the same object. -
Immutability
helps prevent such issues by ensuring that once an object is created, it remains unchanged. This simplifies program flow and debugging.
What is the difference between the var
and let
keywords in JavaScript?
The var
and let
keywords are both used to declare variables in JavaScript, but they have some key differences.
Scope
The main difference between var
and let
is the scope of the variables they create. Variables declared with var
have function scope, which means they are accessible throughout the function in which they are declared. Variables declared with let have block scope, which means they are only accessible within the block where they are declared.
Hoisting
Another difference between var
and let
is that var
declarations are hoisted
, while let declarations are not. Hoisting means that the declaration of a variable is moved to the top of the scope in which it is declared, even though it is not actually executed until later.
Redeclaration
Finally, var
declarations can be redeclared, while let
declarations cannot. This means that you can declare a variable with the same name twice in the same scope with var
, but you will get an error if you try to do the same with let
.
What are object prototypes
?
Object prototypes
in JavaScript are a fundamental concept that enables inheritance and the sharing of properties and method objects.
The prototype object
can have its own prototype. Forming a chain known as the prototype chain.