What are closures in JavaScript, and how do they work?
Explain the concept of hoisting in JavaScript.
How do you create and use a JavaScript prototype?
What are the differences between '==' and '===' operators in JavaScript?
Explain the various types of event propagation (bubbling, capturing, event delegation) in JavaScript.
Describe the difference between 'null' and 'undefined' in JavaScript.
What is the purpose of the 'this' keyword in JavaScript, and how does it work?
How do you create a deep copy of an object in JavaScript?
Explain the concept of promises and async/await in JavaScript.
What are the main differences between ES5 and ES6 (ECMAScript 2015)?
Describe the 'map', 'filter', and 'reduce' functions in JavaScript.
How does JavaScript's garbage collection work?
Explain the use of the 'bind', 'call', and 'apply' methods in JavaScript.
What are JavaScript modules, and how do you import and export them?
How do you handle exceptions in JavaScript using 'try', 'catch', and 'finally'?
What is the difference between a function declaration and a function expression?
Explain the concept of memoization in JavaScript.
What is the purpose of 'use strict' in JavaScript, and what are its benefits?
Describe the differences between the 'let', 'const', and 'var' keywords in JavaScript.
What are higher-order functions, and how are they used in JavaScript?
Answers
Closures: Closures are functions that have access to their own scope, the scope of the outer (enclosing) function, and the global scope. They enable data encapsulation and are based on lexical scoping.
function outerFunction() { let count = 0; function innerFunction() { count++; return count; } return innerFunction; } const counter = outerFunction(); console.log(counter()); // 1 console.log(counter()); // 2 console.log(counter()); // 3
Hoisting: Hoisting is JavaScript's behavior of moving variable and function declarations to the top of their containing scope during the compilation phase. This allows variables and functions to be used before they are declared, but only for declarations, not initializations.
console.log(x); // Output: undefined var x = 5; helloWorld(); // Output: "Hello, World!" function helloWorld() { console.log("Hello, World!"); }
Prototypes: Prototypes are the mechanism by which JavaScript objects inherit features from one another. Objects can have a prototype object, which acts as a template for sharing properties and methods across instances.
In JavaScript, you can create an object with a prototype using either the Object Literal or Prototypes.
Object Literal: The first method of creating an object is by using the Object Literal. It describes the methods and properties of the object that will be inherited directly.
let person = { name: "John", age: 30, sayHello: function() { console.log("Hello!"); } };
Prototypes: Every JavaScript function has a default prototype object. This prototype can be used for creating an object by initializing methods and properties.
function Person(name, age) { this.name = name; this.age = age; } Person.prototype.sayHello = function() { console.log("Hello!"); }; let person1 = new Person("John", 30); let person2 = new Person("Jane", 25); person1.sayHello()//Hello!
'==' vs '===': '==' is a loose equality operator that compares values after performing type coercion, while '===' is a strict equality operator that compares both value and type without performing any type coercion.
let x = 5; let y = "5"; console.log(x == y); // true console.log(x === y); // false
Event propagation: Event propagation is the process by which events bubble up (event bubbling) or trickle down (event capturing) the DOM tree. Event delegation is a technique used to handle events efficiently by attaching a single event listener to a parent element.
Bubbling is the default event propagation mechanism in JavaScript. When an event occurs on an element, it first runs the handlers on that element and then runs the handlers on its parent elements.
Capturing is the opposite of bubbling. When an event occurs on an element, it first runs the handlers on its parent elements and then runs the handlers on that element.
Event delegation is a technique where you add a single event listener to a parent element instead of adding multiple event listeners to its child elements. This technique is useful when you have a large number of child elements that need to handle the same event.
Here’s an example:
<div id="parent"> <div id="child" class="child">Click me</div> </div>
// Bubbling document.querySelector("#child").addEventListener("click", function() { console.log("Child clicked"); }); document.querySelector("#parent").addEventListener("click", function() { console.log("Parent clicked"); }); // Capturing document.querySelector("#child").addEventListener( "click", function() { console.log("Child clicked"); }, true ); document.querySelector("#parent").addEventListener( "click", function() { console.log("Parent clicked"); }, true ); // Event delegation document.querySelector("#parent").addEventListener("click", function(event) { if (event.target && event.target.matches(".child")) { console.log("Child clicked"); } });
'null' vs 'undefined': 'null' is an assignment value that represents no value or no object, while 'undefined' represents a variable that has been declared but has not yet been assigned a value.
'this': 'this' is a keyword that refers to the current object or context within a function. In global scope, 'this' refers to the global object; in object methods, it refers to the object itself.
Deep copy: A deep copy is a process of creating a new object with its own copies of all nested objects and properties, not just references to the original ones. This can be achieved using methods such as JSON.parse(JSON.stringify(obj)), spread operator or using a library like Lodash.
const obj = { a: 1, b: { c: 2 } }; const deepCopy = JSON.parse(JSON.stringify(obj));
const obj = { a: 1, b: { c: 2 } }; const deepCopy = { ...obj };
Promises and async/await: Promises and async/await are techniques for handling asynchronous operations in JavaScript. Promises represent eventual completion (or failure) of an asynchronous operation, while async/await is syntactic sugar for working with promises in a more readable way.
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('foo'); }, 300); }); promise1.then((value) => { console.log(value); // expected output: "foo" }); async function example() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("done!"), 2000) }); let result = await promise; // wait until the promise resolves (*) alert(result); // "done!" } example();
ES5 vs ES6: ES6 (ECMAScript 2015) introduced new features and improvements over ES5, including let and const, arrow functions, template literals, classes, modules, and more.
'map', 'filter', 'reduce': These are higher-order functions that perform operations on arrays. 'map' applies a given function to each element in an array, 'filter' creates a new array with elements that pass a given condition, and 'reduce' accumulates a single value from an array based on a provided function.
const numbers = [1, 2, 3, 4]; const doubledNumbers = numbers.map((number) => number * 2); console.log(doubledNumbers); // expected output: [2, 4, 6, 8] const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = words.filter((word) => word.length > 6); console.log(result); // expected output: ["exuberant", "destruction", "present"] const numbers2 = [0, 1, 2, 3]; const sum = numbers2.reduce((accumulator, currentValue) => accumulator + currentValue); console.log(sum); // expected output: 6
Garbage collection: JavaScript's garbage collection mechanism automatically frees up memory by removing objects that are no longer reachable or referenced by the program.
'bind', 'call', 'apply': These methods are used to manipulate the 'this' context of a function. 'bind' creates a new function with a specified 'this' context, while 'call' and 'apply' invoke a function with a specified 'this' context and arguments.
const person = { firstName: 'John', lastName: 'Doe' }; function greet() { console.log(`Hello, ${this.firstName} ${this.lastName}!`); } greet.call(person); // Hello, John Doe! greet.apply(person); // Hello, John Doe! const greetPerson = greet.bind(person); greetPerson(); // Hello, John Doe!
Modules: JavaScript modules are a way to organize and encapsulate code by exporting and importing functions, objects, or values. Modules help create maintainable and reusable code.
'try', 'catch', 'finally': These statements are used to handle exceptions in JavaScript. 'try' contains code that might throw an exception, 'catch' catches and handles the exception, and 'finally' contains code that runs regardless of whether an exception was caught or not.
try { // code that might throw an error } catch (error) { // code to handle the error } finally { // code that will be executed regardless of whether an error occurred or not }
Function declaration vs function expression: A function declaration is a statement that defines a function using the 'function' keyword, a name, parameters, and a function body. It is hoisted, allowing it to be used before its definition in the code. A function expression is a function assigned to a variable, either as an anonymous function or a named function. Function expressions are not hoisted and can only be used after they are defined.
// Function declaration function add(x, y) { return x + y; } // Function expression const add = function(x, y) { return x + y; };
Memoization: Memoization is an optimization technique that involves caching the results of expensive function calls to avoid redundant computations. It is particularly useful for recursive functions or functions with repetitive inputs.
function memoize(fn) { const cache = {}; return function(...args) { const key = JSON.stringify(args); if (cache[key]) { return cache[key]; } const result = fn(..args); cache[key] = result; return result; }; } const add = memoize(function(x, y) { console.log('called'); return x + y; }); console.log(add(1, 2)); // called 3 console.log(add(1, 2)); // 3
'use strict': 'use strict' is a directive that enforces strict mode in JavaScript, which disallows certain error-prone behaviors and makes debugging easier. Strict mode helps catch common mistakes, such as using undeclared variables, and prohibits the use of some problematic language features.
"use strict"; x = 3.14; // This will cause an error (x is not defined)
'let', 'const', 'var': These are variable declaration keywords in JavaScript. 'var' is function-scoped and is hoisted. 'let' and 'const' are block-scoped, with 'let' allowing variable reassignment and 'const' enforcing read-only variables.
Higher-order functions: Higher-order functions are functions that either take other functions as arguments or return functions as their results. They are commonly used in functional programming for abstraction, composition, and code reusability. Examples include 'map', 'filter', and 'reduce'.
function customMap(arr, func) { const result = []; for (let i = 0; i < arr.length; i++) { result.push(func(arr[i])); } return result; } const result = customMap([1,2,3], (item)=>item+2) console.log(result)//[3,4,5]