26/02 Flashcards
(44 cards)
Explain the concept of tree shaking in module bundling
Tree shaking is a technique used in module bundling to eliminate dead code, which is code that is never used or executed.
This helps to reduce the final bundle size and improve application performance. It works by analyzing the dependency graph of the code and removing any unused exports. Tools like Webpack and Rollup support tree shaking when using ES6 module syntax (import and export).
How tree shaking works
Tree shaking works by analyzing the dependency graph of the code. It looks at the import and export statements to determine which parts of the code are actually used and which are not. The unused code, also known as dead code, is then removed from the final bundle.
Requirements for tree shaking
ES6 module syntax: Tree shaking relies on the static structure of ES6 module syntax (import and export). CommonJS modules (require and module.exports) are not statically analyzable and thus not suitable for tree shaking.
Bundler support: The bundler you are using must support tree shaking. Both Webpack and Rollup have built-in support for tree shaking.
Benefits of tree shaking
Reduced bundle size: By removing unused code, the final bundle size is reduced, leading to faster load times.
Improved performance: Smaller bundles mean less code to parse and execute, which can improve the performance of your application.
What is the difference between ==
and ===
in JavaScript?
== is the abstract equality operator while === is the strict equality operator.
The == operator will compare for equality after doing any necessary type conversions.
The === operator will not do type conversion, so if two values are not the same type === will simply return false.
As a general rule of thumb, never use the == operator, except for convenience when comparing against null or undefined, where a == null will return true if a is null or undefined.
There’s also Object.is(value1, value2).
What’s the difference between a JavaScript variable that is: null
, undefined
or undeclared?
Undeclared
Undeclared variables are created when you assign a value to an identifier that is not previously created using var, let or const. Undeclared variables will be defined globally, outside of the current scope. In strict mode, a ReferenceError will be thrown when you try to assign to an undeclared variable. Undeclared variables are bad in the same way that global variables are bad. Avoid them at all cost! To check for them, wrap its usage in a try/catch block.
function foo() {
x = 1; // Throws a ReferenceError in strict mode
}
foo();
console.log(x); // 1
Using the typeof operator on undeclared variables will give ‘undefined’.
undefined
A variable that is undefined is a variable that has been declared, but not assigned a value. It is of type undefined. If a function does not return any value as the result of executing it is assigned to a variable, the variable also has the value of undefined. To check for it, compare using the strict equality (===) operator or typeof which will give the ‘undefined’ string. Note that you should not be using the loose equality operator (==) to check, as it will also return true if the value is null.
let foo;
console.log(foo); // undefined
console.log(foo === undefined); // true
console.log(typeof foo === ‘undefined’); // true
null
A variable that is null will have been explicitly assigned to the null value. It represents no value and is different from undefined in the sense that it has been explicitly assigned. To check for null, simply compare using the strict equality operator. Note that like the above, you should not be using the loose equality operator (==) to check, as it will also return true if the value is undefined.
const foo = null;
console.log(foo === null); // true
console.log(typeof foo === ‘object’); // true
Notes
As a good habit, never leave your variables undeclared or unassigned. Explicitly assign null to them after declaring if you don’t intend to use them yet.
Always explicitly declare variables before using them to prevent errors.
What’s the difference between .call
and .apply
in JavaScript?
TL;DR
.call and .apply are both used to invoke functions with a specific this context and arguments. The primary difference lies in how they accept arguments:
.call(thisArg, arg1, arg2, …): Takes arguments individually.
.apply(thisArg, [argsArray]): Takes arguments as an array.
An easy way to remember this is C for call and comma-separated and A for apply and an array of arguments.
What is the difference between mouseenter
and mouseover
event in JavaScript and browsers?
The main difference lies in the bubbling behavior of mouseenter and mouseover events. mouseenter does not bubble while mouseover bubbles.
mouseenter event:
Does not bubble: The mouseenter event does not bubble. It is only triggered when the mouse pointer enters the element to which the event listener is attached, not when it enters any child elements.
Triggered once: The mouseenter event is triggered only once when the mouse pointer enters the element, making it more predictable and easier to manage in certain scenarios.
A use case for mouseenter is when you want to detect the mouse entering an element without worrying about child elements triggering the event multiple times.
mouseover Event:
Bubbles up the DOM: The mouseover event bubbles up through the DOM. This means that if you have an event listener on a parent element, it will also trigger when the mouse pointer moves over any child elements.
Triggered multiple times: The mouseover event is triggered every time the mouse pointer moves over an element or any of its child elements. This can lead to multiple triggers if you have nested elements.
A use case for mouseover is when you want to detect when the mouse enters an element or any of its children and are okay with the events triggering multiple times.
What are the various data types in JavaScript?
TL;DR
In JavaScript, data types can be categorized into primitive and non-primitive types:
Primitive data types
Number: Represents both integers and floating-point numbers.
String: Represents sequences of characters.
Boolean: Represents true or false values.
Undefined: A variable that has been declared but not assigned a value.
Null: Represents the intentional absence of any object value.
Symbol: A unique and immutable value used as object property keys.
BigInt: Represents integers with arbitrary precision.
Non-primitive (Reference) data types:
Object: Used to store collections of data.
Array: An ordered collection of data.
Function: A callable object.
Date: Represents dates and times.
RegExp: Represents regular expressions.
Map: A collection of keyed data items.
Set: A collection of unique values.
The primitive types store a single value, while non-primitive types can store collections of data or complex entities.
JavaScript is a dynamically-typed language, which means variables can hold values of different data types over time. The typeof operator can be used to determine the data type of a value or variable.
What are Symbol
s used for in JavaScript?
Symbols in JavaScript are a new primitive data type introduced in ES6 (ECMAScript 2015). They are unique and immutable identifiers that is primarily for object property keys to avoid name collisions.
Key characteristics
Uniqueness: Each Symbol value is unique, even if they have the same description.
Immutability: Symbol values are immutable, meaning their value cannot be changed.
Non-enumerable: Symbol properties are not included in for…in loops or Object.keys().
Global Symbol registry
You can create global Symbols using Symbol.for(‘key’), which creates a new Symbol in the global registry if it doesn’t exist, or returns the existing one. This allows you to reuse Symbols across different parts of your code base or even across different code bases.
What is the difference between a Map
object and a plain object in JavaScript?
Both Map objects and plain objects in JavaScript can store key-value pairs, but they have several key differences:
Plain JavaScript objects (POJO)
A plain object is a basic JavaScript object created using the {} syntax. It is a collection of key-value pairs, where each key is a string (or a symbol, in modern JavaScript) and each value can be any type of value, including strings, numbers, booleans, arrays, objects, and more.
Map objects
A Map object, introduced in ECMAScript 2015 (ES6), is a more advanced data structure that allows you to store key-value pairs with additional features. A Map is an iterable, which means you can use it with for…of loops, and it provides methods for common operations like get, set, has, and delete.
Key differences
Here are the main differences between a Map object and a plain object:
Key types: In a plain object, keys are always strings (or symbols).
In a Map, keys can be any type of value, including objects, arrays, and even other Maps.
Key ordering: In a plain object, the order of keys is not guaranteed.
In a Map, the order of keys is preserved, and you can iterate over them in the order they were inserted.
Iteration: A Map is iterable, which means you can use for…of loops to iterate over its key-value pairs.
A plain object is not iterable by default, but you can use Object.keys() or Object.entries() to iterate over its properties.
Performance: Map objects are generally faster and more efficient than plain objects, especially when dealing with large datasets.
Methods: A Map object provides additional methods, such as get, set, has, and delete, which make it easier to work with key-value pairs.
Serialization: When serializing a Map object to JSON, it will be converted to an object but the existing Map properties might be lost in the conversion. A plain object, on the other hand, is serialized to a JSON object with the same structure.
When to use POJO vs Map?
When to use which
Use a plain object (POJO) when:
You need a simple, lightweight object with string keys.
You’re working with a small dataset.
You need to serialize the object to JSON (e.g. to send over the network).
Use a Map object when:
You need to store key-value pairs with non-string keys (e.g., objects, arrays).
You need to preserve the order of key-value pairs.
You need to iterate over the key-value pairs in a specific order.
You’re working with a large dataset and need better performance.
In summary, while both plain objects and Map objects can be used to store key-value pairs, Map objects offer more advanced features, better performance, and additional methods, making them a better choice for more complex use cases.
What are proxies in JavaScript used for?
In JavaScript, a proxy is an object that acts as an intermediary between an object and the code. Proxies are used to intercept and customize the fundamental operations of JavaScript objects, such as property access, assignment, function invocation, and more.
Here’s a basic example of using a Proxy to log every property access:
const myObject = {
name: ‘John’,
age: 42,
};
const handler = {
get: function (target, prop, receiver) {
console.log(Someone accessed property "${prop}"
);
return target[prop];
},
};
const proxiedObject = new Proxy(myObject, handler);
console.log(proxiedObject.name); // ‘John’
// Someone accessed property “name”
console.log(proxiedObject.value); // 42
// Someone accessed property “value”
This is useful for creating wrappers for logging and debugging interactions with an object.
Proxies can be used to validate property values before they are set on the target object.
Proxies are often used to trigger updates in other parts of your application when object properties change (data binding).
A practical example is JavaScript frameworks like Vue.js, where proxies are used to create reactive systems that automatically update the UI when data changes.
Explain the concept of a callback function in asynchronous operations
A callback function is a function passed as an argument to another function, which is then invoked inside the outer function to complete some kind of routine or action. In asynchronous operations, callbacks are used to handle tasks that take time to complete, such as network requests or file I/O, without blocking the execution of the rest of the code. For example:
A callback function is a function passed as an argument to another function, which is then invoked inside the outer function to complete some kind of routine or action. In asynchronous operations, callbacks are used to handle tasks that take time to complete, such as network requests or file I/O, without blocking the execution of the rest of the code. For example:
Common use cases
Network requests: Fetching data from an API
File I/O: Reading or writing files
Timers: Delaying execution using setTimeout or setInterval
Event handling: Responding to user actions like clicks or key presses
Explain the concept of a microtask queue
The microtask queue is a queue of tasks that need to be executed after the currently executing script and before any other task. Microtasks are typically used for tasks that need to be executed immediately after the current operation, such as promise callbacks. The microtask queue is processed before the macrotask queue, ensuring that microtasks are executed as soon as possible.
How does the microtask queue work?
Execution order: The microtask queue is processed after the currently executing script and before the macrotask queue. This means that microtasks are given higher priority over macrotasks.
Event loop: During each iteration of the event loop, the JavaScript engine first processes all the microtasks in the microtask queue before moving on to the macrotask queue.
Adding microtasks: Microtasks can be added to the microtask queue using methods like Promise.resolve().then() and queueMicrotask().
Use cases
Promise callbacks: Microtasks are commonly used for promise callbacks to ensure they are executed as soon as possible after the current operation.
MutationObserver: The MutationObserver API uses microtasks to notify changes in the DOM.
Explain the concept of caching and how it can be used to improve performance
Caching is a technique used to store copies of files or data in a temporary storage location to reduce the time it takes to access them. It improves performance by reducing the need to fetch data from the original source repeatedly. In front end development, caching can be implemented using browser cache, service workers, and HTTP headers like Cache-Control.
What is browser caching?
The browser cache stores copies of web pages, images, and other resources locally on the user’s device. When a user revisits a website, the browser can load these resources from the cache instead of fetching them from the server, resulting in faster load times.
<head>
<link></link>
</head>
What are Service workers?
Service workers are scripts that run in the background and can intercept network requests. They can cache resources and serve them from the cache, even when the user is offline. This can significantly improve performance and provide a better user experience.
self.addEventListener(‘install’, (event) => {
event.waitUntil(
caches.open(‘v1’).then((cache) => {
return cache.addAll([‘/index.html’, ‘/styles.css’, ‘/app.js’]);
}),
);
});
self.addEventListener(‘fetch’, (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
}),
);
});
What is HTTP caching?
HTTP caching involves using HTTP headers to control how and when resources are cached. Common headers include Cache-Control, Expires, and ETag.
Cache-Control: max-age=3600
How does caching improve performance?
Reduced latency
By storing frequently accessed data closer to the user, caching reduces the time it takes to retrieve that data. This results in faster load times and a smoother user experience.
Reduced server load
Caching reduces the number of requests made to the server, which can help decrease server load and improve overall performance.
Offline access
With service workers, cached resources can be served even when the user is offline, providing a seamless experience.
Explain the concept of code coverage and how it can be used to assess test quality
Code coverage is a metric that measures the percentage of code that is executed when the test suite runs. It helps in assessing the quality of tests by identifying untested parts of the codebase. Higher code coverage generally indicates more thorough testing, but it doesn’t guarantee the absence of bugs. Tools like Istanbul or Jest can be used to measure code coverage.
Types of code coverage
Statement coverage: Measures the number of statements in the code that have been executed.
Branch coverage: Measures whether each branch (e.g., if and else blocks) has been executed.
Function coverage: Measures whether each function in the code has been called.
Line coverage: Measures the number of lines of code that have been executed.
Condition coverage: Measures whether each boolean sub-expression has been evaluated to both true and false.
Pros/cons of code coverage?
Benefits
Identifies untested code: Helps in finding parts of the codebase that are not covered by tests.
Improves test suite: Encourages writing more comprehensive tests.
Increases confidence: Higher coverage can increase confidence in the stability of the code.
Limitations
False sense of security: High coverage does not guarantee the absence of bugs.
Quality over quantity: 100% coverage does not mean the tests are of high quality. Tests should also check for edge cases and potential errors.
Explain the concept of Content Security Policy (CSP) and how it enhances security?
Content Security Policy (CSP) is a security feature that helps prevent various types of attacks, such as Cross-Site Scripting (XSS) and data injection attacks, by specifying which content sources are trusted. It works by allowing developers to define a whitelist of trusted sources for content like scripts, styles, and images. This is done through HTTP headers or meta tags. For example, you can use the Content-Security-Policy header to specify that only scripts from your own domain should be executed:
Content-Security-Policy: script-src ‘self’
What is Content Security Policy (CSP)?
Content Security Policy (CSP) is a security standard introduced to mitigate a range of attacks, including Cross-Site Scripting (XSS) and data injection attacks. CSP allows web developers to control the resources that a user agent is allowed to load for a given page. By specifying a whitelist of trusted content sources, CSP helps to prevent the execution of malicious content.
How does CSP work?
CSP works by allowing developers to define a set of rules that specify which sources of content are considered trustworthy. These rules are delivered to the browser via HTTP headers or meta tags. When the browser loads a page, it checks the CSP rules and blocks any content that does not match the specified sources.
Example of a CSP header
Here is an example of a simple CSP header that only allows scripts from the same origin:
Content-Security-Policy: script-src ‘self’
Common directives
default-src: Serves as a fallback for other resource types when they are not explicitly defined.
script-src: Specifies valid sources for JavaScript.
style-src: Specifies valid sources for CSS.
img-src: Specifies valid sources for images.
connect-src: Specifies valid sources for AJAX, WebSocket, and EventSource connections.
font-src: Specifies valid sources for fonts.
object-src: Specifies valid sources for plugins like Flash.