JavaScript is a high-level, interpreted scripting language used for web development. Unlike languages like Java or C++, JavaScript is lightweight, dynamic, and primarily used for client-side programming, although it can also run server-side with Node.js.
JavaScript supports seven primitive data types: string, number, boolean, null, undefined, symbol, and bigint. Additionally, it has objects as a non-primitive type.
var is function-scoped and can be re-declared or updated. let is block-scoped and can be updated but not re-declared. const is also block-scoped but cannot be updated or re-declared.
JavaScript automatically converts data types when necessary, known as type coercion. For example, 5 + '5' results in the string "55". To avoid unintended coercion, strict equality (===) is recommended.
A closure is a function that retains access to its outer scope, even after the outer function has returned. Closures enable private variables and functions.
Global variables can lead to naming conflicts and unpredictable behavior, especially in large codebases, because they are accessible from anywhere in the program.
Code quality can be ensured through linting tools like ESLint, writing unit tests, following coding conventions, and using version control for collaborative development.
'use strict'; enforces stricter parsing and error handling in JavaScript, such as disallowing the use of undeclared variables and preventing silent errors.
Best practices include following the DRY (Don't Repeat Yourself) principle, writing modular code, using meaningful variable names, and adding comments for clarity.
Deep nesting should be avoided by breaking down functions into smaller, reusable components or using techniques like early return statements, promises, or async/await to flatten the code.
Objects can be created using object literals {}, constructor functions, or the new Object() syntax. For example, const person = { name: 'John', age: 30 };.
Dot notation (object.property) is used when the property name is a valid identifier. Bracket notation (object['property']) is used when the property name is dynamic or not a valid identifier.
You can add properties using either dot or bracket notation (object.property = value;). To remove a property, use the delete keyword (delete object.property;).
JavaScript provides methods like Object.keys(), Object.values(), Object.entries(), and Object.assign() for working with objects.
Object destructuring allows you to extract multiple properties from an object in a concise way. Example: const { name, age } = person;.
JavaScript has seven primitive data types: string, number, boolean, null, undefined, symbol, and bigint.
undefined means a variable has been declared but has not been assigned a value, whereas null is an intentional assignment representing the absence of a value.
Falsy values are values that evaluate to false in a Boolean context (e.g., 0, null, undefined, false, NaN, ""). All other values are considered truthy.
JavaScript automatically converts values to the expected type (type coercion), or developers can use explicit methods like Number(), String(), and Boolean() for manual conversion.
NaN stands for "Not-a-Number" and represents an invalid number operation. You can check for it using isNaN() or Number.isNaN() for stricter checks.
A higher-order function is a function that takes another function as an argument or returns a function as its result. Examples include map(), filter(), and reduce().
call() and apply() invoke a function with a specified this value and arguments. The difference is that apply() takes an array of arguments. bind() creates a new function with the specified this value.
Arrow functions (() => {}) provide a shorter syntax and do not have their own this, arguments, or super, making them unsuitable for use as methods or constructors.
Currying is a technique where a function that takes multiple arguments is transformed into a sequence of functions, each taking a single argument. Example: f(a, b) becomes f(a)(b).
You can define default parameter values in functions like this: function greet(name = 'Guest') { console.log('Hello ' + name); }.
Property descriptors allow you to define specific behaviors for object properties, such as making them writable, enumerable, or configurable.
Use the Object.defineProperty() method and set the writable property to false. Example: Object.defineProperty(obj, 'prop', { value: 42, writable: false });.
The enumerable attribute determines whether a property can be included in loops like for...in or in functions like Object.keys().
You can use Object.preventExtensions(obj) to prevent new properties from being added, or Object.freeze(obj) to make the object immutable.
Getters and setters can be defined using Object.defineProperty(), or within an object literal using get and set. Example: { get name() { return this._name; }, set name(value) { this._name = value; } }.
A prototype is an object from which other objects inherit properties and methods. In JavaScript, every object has a prototype, accessible via Object.getPrototypeOf() or the _proto_ property.
JavaScript uses prototypal inheritance, where objects inherit properties and methods from their prototypes. Objects can be linked via Object.create() or constructor functions.
The prototype property on functions is used to add methods and properties that instances of that function’s constructor will inherit. For example, function Person() {} would have Person.prototype.
You can create an object that inherits from another object using Object.create().
Example: let obj = Object.create(protoObj);.
_proto_ is the actual object from which another object inherits, while prototype is a property of constructor functions that is used to build _proto_ for instances.
You define a class in JavaScript using the class keyword.
Example: class Person { constructor(name) { this.name = name; } }.
The constructor method is used for initializing an object's properties when a new instance of the class is created.
Inheritance is implemented using the extends keyword.
Example: class Child extends Parent {}.
The super keyword is used to call the constructor or methods of a parent class.
Example: super() in a child class constructor calls the parent class’s constructor.
Yes, static methods are defined using the static keyword, and they belong to the class itself rather than any instance. Example: static greet() { console.log('Hello'); }.
A try...catch block is used to handle runtime errors gracefully without stopping the program. Code in the try block is executed, and if an error occurs, it is caught in the catch block.
The finally block executes after the try and catch blocks, regardless of whether an error was thrown, ensuring cleanup operations.
You can throw custom errors using the throw keyword.
Example: throw new Error('Something went wrong');.
Synchronous errors are handled with try...catch, while asynchronous errors (like in Promises) require .catch() for rejection or async/await with try...catch.
You can create a custom error class by extending the built-in Error class.
Example: class MyError extends Error { constructor(message) { super(message); } }.
A Promise is an object that represents the eventual completion or failure of an asynchronous operation, providing methods like then(), catch(), and finally() for handling results.
A Promise is created using the Promise constructor, which takes a function with resolve and reject parameters
Example: new Promise((resolve, reject) => { /* async code */ });.
async is used to declare asynchronous functions, and await pauses the function execution until a Promise is resolved, simplifying asynchronous code.
Errors in async/await functions are handled using try...catch blocks around the await expression
Promise.all() resolves when all promises in the array resolve, while Promise.race() resolves or rejects as soon as the first promise in the array settles.
A generator function is a function that can pause its execution and resume later, yielding values over time. It is defined using function* syntax.
You use the yield keyword to pause a generator and the next() method to resume it.
Example: function* gen() { yield 1; yield 2; }.
The return() method in a generator function immediately ends the generator, and subsequent calls to next() return done: true.
You can iterate over a generator using for...of loops or by calling the next() method manually to get the yielded values.
Generators allow for lazy evaluation, meaning values are generated on-demand, which can save memory and improve performance in scenarios like processing large datasets.
A module is a file or a piece of code that can be reused across a JavaScript application. Modules encapsulate code and expose it using export and import statements.
How do you export a function from a module?
Example: export function greet() { console.log('Hello'); }.
You import a function using the import keyword.
Example: import { greet } from './module.js';.
Named exports export multiple values from a module, while default exports allow you to export a single value or function.
Example: export default function() {}.
Tree-shaking is a technique used by bundlers to remove unused code from modules during the build process, reducing the final bundle size.
The document object represents the HTML document that is displayed in the browser window. It allows developers to manipulate the content and structure of the webpage.
You can select an element by its ID using document.getElementById('id').
document.createElement() is used to create a new HTML element programmatically, which can then be added to the DOM.
You can change the content of an HTML element using innerHTML, textContent, or innerText. Example: element.innerHTML = 'New content';.
The DOMContentLoaded event fires when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, or subframes to finish loading.
Events are actions or occurrences (like clicks or keypresses) that happen in the browser and can be handled using event listeners.
You can add an event listener using addEventListener().
Example: element.addEventListener('click', function() { alert('Clicked!'); });.
Event delegation is a technique where a single event listener is added to a parent element to handle events from its child elements, improving performance and reducing code duplication.
You can prevent the default behavior of an event using event.preventDefault().
Example: in a form submission event, calling this method stops the form from submitting.
Event bubbling is when an event starts at the target element and propagates up to the parent elements. You can stop it using event.stopPropagation().
A click event is triggered when a user clicks on an HTML element, typically a button or a link.
The mousedown event fires when the mouse button is pressed down, while the mouseup event fires when the button is released.
You can detect keyboard events using the keydown, keypress, or keyup events, and access the pressed key via event.key.
You can listen for window resizing using the resize event.
Example: window.addEventListener('resize', function() { console.log('Window resized'); });.
The input event is triggered when the value of an <input>, <textarea>, or <select> element changes, providing real-time updates as the user types.
You can submit a form programmatically using the form.submit() method or by adding a submit event listener to handle form submissions.
You can prevent form submission by calling event.preventDefault() inside the form’s submit event listener.
Form data can be accessed using the FormData object, which collects the form’s key-value pairs.
Example: let formData = new FormData(formElement);.
The action attribute specifies the URL where the form’s data will be sent when it is submitted
You can validate form input by checking the values of form fields and providing feedback.
Example: if (inputField.value === '') { alert('Field is required'); }.
requestAnimationFrame() is used to create smooth animations by calling the provided callback function before the next repaint of the page, improving performance.
You can animate an element’s style by changing its CSS properties incrementally over time, often using setInterval() or requestAnimationFrame().
CSS animations are declarative and rely on keyframes, while JavaScript animations give more control over individual animation steps and allow for conditional logic.
A fade-in effect can be created by gradually increasing the opacity property of an element over time using a setInterval() loop or requestAnimationFrame().
Easing controls the rate of change of an animation over time, making the animation appear more natural. Common easing functions include ease-in, ease-out, and ease-in-out.
You can open a new browser window using window.open('URL', '_blank'), which opens the URL in a new tab or window.
Communication between different windows or iframes is achieved using the window.postMessage() method for secure message passing.
window.parent references the parent window of an iframe, allowing access to the parent window’s properties and methods.
You can resize a window using the window.resizeTo() or window.resizeBy() methods, specifying the desired dimensions.
A browser window can be closed using window.close(), but only if the window was opened by JavaScript (due to security restrictions).
Files can be read using the FileReader API, which provides methods like readAsText() and readAsDataURL() to read file contents asynchronously.
A Blob (Binary Large Object) represents immutable raw data, such as files. You can create a blob using new Blob() and pass in an array of data.
You can trigger a file download by creating a link element with a download attribute and simulating a click.
Example: a.href = fileURL; a.download = 'file.txt'; a.click();.
ArrayBuffer is used to represent a generic, fixed-length binary data buffer. It is often used in combination with TypedArray views to manipulate binary data.
File uploads are handled by selecting a file using an < input type="file"> element, and then the file can be accessed and sent to the server using FormData.
The fetch API is used to make asynchronous HTTP requests to servers and returns a Promise, simplifying the process of sending and receiving data compared to XMLHttpRequest.
You can send a GET request using fetch(url) and handling the returned Promise.
Example: fetch('https://api.example.com/data').then(response => response.json());.
Errors in a fetch request can be handled using .catch() to catch any network errors or exceptions.
CORS (Cross-Origin Resource Sharing) is a security mechanism that restricts web pages from making requests to different domains unless the server explicitly allows it.
You can send data in a POST request by passing an options object to fetch(), specifying the method and body. Example: fetch(url, { method: 'POST', body: JSON.stringify(data) });.
localStorage is a web storage object that allows you to store key-value pairs in the browser with no expiration date, making it persistent even after the browser is closed.
localStorage persists data until explicitly deleted, whereas sessionStorage stores data for the duration of the page session and is cleared when the tab is closed.
You can store data using localStorage.setItem('key', 'value') and retrieve it with localStorage.getItem('key').
You can remove an item from localStorage using localStorage.removeItem('key'), or clear all storage with localStorage.clear().
localStorage typically has a size limit of about 5MB per domain, depending on the browser.
Web components are a set of standardized APIs that allow developers to create reusable custom elements with their own encapsulated HTML, CSS, and JavaScript.
A custom element is defined using the customElements.define() method, which associates a class with a new HTML tag.
Example: customElements.define('my-element', MyElementClass);.
The shadow DOM is a feature of web components that encapsulates the internal structure and styles of a component, preventing them from affecting or being affected by the rest of the document.
The three parts of a web component are custom elements, shadow DOM, and HTML templates.
You attach a shadow DOM to an element using element.attachShadow({ mode: 'open' }), which creates a shadow root for the element.
A regular expression (regex) is a sequence of characters that define a search pattern, used for pattern matching and text manipulation.
You can create a regular expression using literal syntax (/pattern/flags) or the RegExp() constructor
Example: /\d+/ matches one or more digits.