JavaScript Notes
Practice 50 carefully crafted JavaScript multiple choice questions covering basics, functions, arrays, async programming, and advanced concepts. Perfect for interview preparation and self-assessment.
Practice these 50 JavaScript MCQs to strengthen your understanding of core concepts, prepare for interviews, and test your knowledge across all major topics. Each question includes a detailed explanation so you can learn from every answer.
Tip: Try to answer each question yourself before revealing the answer. This active recall technique significantly improves retention.
Q2. Which of the following is NOT a primitive data type in JavaScript?
A) Symbol B) BigInt C) Array D) undefined
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: C) Array
JavaScript has 7 primitive types: string, number, boolean, undefined, null, symbol, and bigint. Arrays are objects (reference types), not primitives. You can verify this with typeof [] === "object".
</details>
Q3. What is the output?
console.log(0.1 + 0.2 === 0.3);A) true B) false C) NaN D) TypeError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) false
Due to floating-point precision in IEEE 754 representation, 0.1 + 0.2 evaluates to 0.30000000000000004, not exactly 0.3. This is a common gotcha in JavaScript (and most programming languages). Use Math.abs(a - b) < Number.EPSILON for safe comparisons.
</details>
Q4. What does "5" + 3 evaluate to?
A) 8 B) "53" C) "8" D) NaN
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) "53"
When the + operator encounters a string operand, it performs string concatenation rather than numeric addition. The number 3 is coerced to the string "3", resulting in "53". This is called type coercion.
</details>
Q5. What is the output?
let x;
console.log(x);A) null B) 0 C) "" D) undefined
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: D) undefined
Variables declared with let (or var) but not assigned a value are automatically initialized to undefined. This is different from null, which must be explicitly assigned.
</details>
Q6. Which statement about const is correct?
A) const makes objects immutable B) const variables cannot be reassigned C) const has function scope D) const variables are hoisted and accessible before declaration
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) const variables cannot be reassigned
const prevents reassignment of the variable binding, but it does NOT make objects immutable. You can still modify properties of a const object. const is block-scoped (not function-scoped) and is hoisted but remains in the Temporal Dead Zone (TDZ) until the declaration is reached.
</details>
Q7. What is the output?
console.log(1 == "1");
console.log(1 === "1");A) true true B) true false C) false true D) false false
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) true false
The == operator performs type coercion before comparison, so "1" is converted to 1. The === operator (strict equality) does not perform type coercion — since the types differ (number vs string), it returns false.
</details>
Q8. What is the output?
console.log(typeof NaN);A) "NaN" B) "undefined" C) "number" D) "object"
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: C) "number"
Despite its name ("Not a Number"), NaN is technically of type number in JavaScript. It represents a numeric value that is undefined or unrepresentable. Use Number.isNaN() to check for NaN values.
</details>
Q9. Which method converts a JSON string into a JavaScript object?
A) JSON.stringify() B) JSON.parse() C) JSON.toObject() D) Object.fromJSON()
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) JSON.parse()
JSON.parse() takes a JSON string and transforms it into a JavaScript object. Conversely, JSON.stringify() converts a JavaScript object into a JSON string. The other options do not exist in standard JavaScript.
</details>
Q10. What is the output?
console.log([] == false);
console.log([] == ![]);A) true true B) true false C) false true D) false false
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: A) true true
For [] == false: the empty array is coerced to "" (empty string), then to 0, and false is coerced to 0. So 0 == 0 is true. For [] == ![]: ![] evaluates to false (since arrays are truthy), then it becomes [] == false, which we just showed is true. This demonstrates the quirks of abstract equality comparison.
</details>
Section 2: Functions & Scope (Questions 11–20)
Q11. What is the output?
function test() {
console.log(a);
var a = 10;
}
test();A) 10 B) undefined C) ReferenceError D) null
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) undefined
Due to hoisting, the declaration var a is moved to the top of the function, but the assignment = 10 stays in place. So at the time of console.log(a), the variable exists but has the value undefined.
</details>
Q12. What is the output?
A) The global object (window or global) B) undefined C) The function itself D) Depends on the enclosing lexical scope
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: D) Depends on the enclosing lexical scope
Arrow functions do not have their own this binding. They inherit this from the enclosing lexical scope at the time they are defined. In a module or strict mode at the top level, this would be undefined. In a browser's non-strict global scope, it would be window.
</details>
Q13. What is a closure in JavaScript?
A) A function that is called immediately after definition B) A function that retains access to its outer scope even after the outer function has returned C) A function that has no parameters D) A function defined inside a class
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) A function that retains access to its outer scope even after the outer function has returned
A closure is created when an inner function "closes over" variables from its outer (enclosing) function. Even after the outer function finishes execution, the inner function retains a reference to those variables through the closure.
</details>
Q14. What is the output?
A) 0 1 2 B) 3 3 3 C) undefined undefined undefined D) 0 0 0
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 3 3 3
Since var is function-scoped (not block-scoped), there is only one i variable shared across all iterations. By the time the setTimeout callbacks execute, the loop has completed and i is 3. Using let instead of var would fix this, as let creates a new binding per iteration.
</details>
Q15. What is the output?
function multiply(a, b = a * 2) {
return a * b;
}
console.log(multiply(3));A) NaN B) 9 C) 18 D) 6
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: C) 18
Default parameters can reference earlier parameters. When multiply(3) is called, a = 3 and b defaults to a * 2 = 6. Therefore, a * b = 3 * 6 = 18.
</details>
Q16. What is the output?
function foo() {
return
{
message: "hello"
};
}
console.log(foo());A) { message: "hello" } B) undefined C) SyntaxError D) null
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) undefined
JavaScript's Automatic Semicolon Insertion (ASI) adds a semicolon after return because the opening brace { is on the next line. The function effectively becomes return; followed by an unreachable block. Always place the opening brace on the same line as return.
</details>
Q17. What is the output?
const obj = {
name: "JS",
getName: function() {
return this.name;
}
};
const getName = obj.getName;
console.log(getName());A) "JS" B) undefined C) TypeError D) ""
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) undefined
When the method is extracted and called as a standalone function, this no longer refers to obj. In non-strict mode, this becomes the global object (which doesn't have a name property, so it returns undefined). In strict mode, this would be undefined and would throw a TypeError.
</details>
Q18. Which of the following correctly creates an IIFE?
A) function(){}() B) (function(){})() C) function(){()} D) {function(){}()}
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) (function(){})()
An IIFE (Immediately Invoked Function Expression) wraps the function in parentheses to make it an expression, then immediately invokes it with (). Option A would cause a syntax error because the parser treats it as a function declaration without a name.
</details>
Q19. What is the output?
A) [1, 2, 3, 4, 5] B) 15 C) NaN D) "12345"
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 15
The rest parameter ...args collects all arguments into an array [1, 2, 3, 4, 5]. The reduce method accumulates the sum starting from 0: 0+1+2+3+4+5 = 15.
</details>
Q20. What is the output?
A) 3 1 B) 2 2 C) 3 2 D) 2 1
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: A) 3 1
The inner function accesses a from outer's scope (closure), incrementing it from 2 to 3. The global a remains 1 because the let a = 2 in outer creates a separate variable that shadows the global one.
</details>
Section 3: Arrays & Objects (Questions 21–30)
Q21. What is the output?
A) [6, 8, 10] B) [2, 4, 6, 8, 10] C) [3, 4, 5] D) [1, 2, 6, 8, 10]
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: A) [6, 8, 10]
filter(x => x > 2) returns [3, 4, 5]. Then map(x => x * 2) doubles each element, resulting in [6, 8, 10]. Method chaining is a common pattern in functional JavaScript.
</details>
Q22. What is the output?
const obj = { a: 1, b: 2, c: 3 };
const { a, ...rest } = obj;
console.log(rest);A) { a: 1 } B) { b: 2, c: 3 } C) [2, 3] D) undefined
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) { b: 2, c: 3 }
Destructuring with the rest operator (...rest) collects all remaining properties that weren't explicitly destructured into a new object. Since a was extracted separately, rest contains { b: 2, c: 3 }.
</details>
Q23. Which method returns a new array without modifying the original?
A) splice() B) push() C) map() D) sort()
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: C) map()
map() creates and returns a new array with the results of calling a function on every element. It does not mutate the original array. splice(), push(), and sort() all modify the original array in place (though sort() also returns the array).
</details>
Q24. What is the output?
A) 4 B) 11 C) 10 D) 3
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 11
Setting arr[10] = 11 extends the array's length to 11 (indices 0–10). Indices 3–9 become "empty slots" (sparse array). The length property is always one more than the highest index.
</details>
Q25. What is the output?
const person = { name: "Alice" };
Object.freeze(person);
person.name = "Bob";
person.age = 30;
console.log(person);A) { name: "Bob", age: 30 } B) { name: "Alice", age: 30 } C) { name: "Alice" } D) TypeError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: C) { name: "Alice" }
Object.freeze() prevents modifications to the object — you cannot add, remove, or change properties. In non-strict mode, attempts to modify silently fail. In strict mode, they would throw a TypeError. Note: freeze is shallow — nested objects are not frozen.
</details>
Q26. What is the output?
const a = { x: 1 };
const b = { x: 1 };
console.log(a === b);
console.log(a === a);A) true true B) false true C) true false D) false false
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) false true
Objects are compared by reference, not by value. Even though a and b have identical properties, they are different objects in memory. Only when comparing an object to itself (a === a) does it return true.
</details>
Q27. What is the output?
A) 0 B) 15 C) [1, 2, 3, 4, 5] D) NaN
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 15
When reduce is called without an initial value, it uses the first element (1) as the initial accumulator and starts iterating from the second element. The computation is: 1+2=3, 3+3=6, 6+4=10, 10+5=15.
</details>
Q28. What is the output?
const original = { a: 1, b: { c: 2 } };
const copy = { ...original };
copy.b.c = 42;
console.log(original.b.c);A) 2 B) 42 C) undefined D) TypeError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 42
The spread operator performs a shallow copy. While copy.a is independent of original.a, the nested object b is shared by reference. Modifying copy.b.c also changes original.b.c. Use structuredClone() or libraries for deep cloning.
</details>
Q29. What does Object.keys({ b: 2, a: 1, c: 3 }) return?
A) ["a", "b", "c"] B) ["b", "a", "c"] C) [2, 1, 3] D) ["b: 2", "a: 1", "c: 3"]
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) ["b", "a", "c"]
Object.keys() returns an array of the object's own enumerable string property names in the same order as a for...in loop. For non-integer string keys, this is insertion order. The keys are returned as strings, not values.
</details>
Q30. What is the output?
A) 1 2 B) 1 3 C) 1 undefined D) SyntaxError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 1 3
Array destructuring allows skipping elements using empty slots (commas). The first element is assigned to first, the second is skipped, and the third is assigned to third. This is a concise way to extract non-consecutive elements.
</details>
Section 4: Async JavaScript (Questions 31–40)
Q31. What is the output?
A) Start End Promise Timeout B) Start End Timeout Promise C) Start Timeout Promise End D) Start Promise End Timeout
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: A) Start End Promise Timeout
The event loop processes microtasks (Promises) before macrotasks (setTimeout). Synchronous code runs first (Start, End), then the microtask queue (Promise), then the macrotask queue (Timeout). Even with a 0ms delay, setTimeout is always deferred.
</details>
Q32. What does async before a function declaration do?
A) Makes the function run in a separate thread B) Makes the function always return a Promise C) Makes the function execute immediately D) Prevents the function from throwing errors
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) Makes the function always return a Promise
An async function always returns a Promise. If the function returns a value, it's wrapped in Promise.resolve(). If it throws, the returned promise is rejected. JavaScript is single-threaded — async does not create new threads.
</details>
Q33. What is the output?
async function getData() {
return "Hello";
}
console.log(getData());A) "Hello" B) Promise { "Hello" } C) undefined D) Promise { <pending> }
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) Promise { "Hello" }
Since getData is an async function, it wraps the return value in a Promise. Logging the function call shows a resolved Promise object. To get the actual value, you need await getData() inside another async function or use .then().
</details>
Q34. What happens when a Promise is rejected and has no .catch() handler?
A) The error is silently ignored B) An UnhandledPromiseRejection warning/error is emitted C) The program immediately crashes D) The Promise automatically resolves to null
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) An UnhandledPromiseRejection warning/error is emitted
In modern Node.js and browsers, unhandled promise rejections trigger a warning or error event. In newer Node.js versions, unhandled rejections can terminate the process. Always attach .catch() handlers or use try/catch with async/await.
</details>
Q35. What is the output?
A) [1, 2, 3] B) 1 C) 3 D) 6
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: A) [1, 2, 3]
Promise.all() takes an array of promises and returns a single promise that resolves with an array of all resolved values, maintaining the original order. If any promise rejects, the entire Promise.all() rejects with that reason.
</details>
Q36. What is the difference between Promise.all() and Promise.allSettled()?
A) They are identical B) Promise.all() short-circuits on rejection; Promise.allSettled() waits for all C) Promise.allSettled() is faster D) Promise.all() only works with two promises
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) Promise.all() short-circuits on rejection; Promise.allSettled() waits for all
Promise.all() rejects immediately if any promise rejects. Promise.allSettled() waits for all promises to settle (resolve or reject) and returns an array of objects with status ("fulfilled" or "rejected") and the corresponding value or reason.
</details>
Q37. What is the output?
A) Done After foo B) After foo Done C) After foo D) Done
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) After foo Done
foo() starts executing and hits await, which suspends the async function. Control returns to the caller, and "After foo" is logged synchronously. After 1 second, the promise resolves and "Done" is logged.
</details>
Q38. What does Promise.race() return?
A) The result of all promises combined B) The result of the first promise to settle (resolve or reject) C) The result of the fastest resolving promise only D) An array of results in completion order
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) The result of the first promise to settle (resolve or reject)
Promise.race() returns a promise that settles with the same result as the first promise to settle, whether it resolves or rejects. It's commonly used for implementing timeouts or racing between multiple data sources.
</details>
Q39. What is the output?
A) true B) false C) undefined D) Error
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: A) true
The two await delay(100) calls run sequentially (not in parallel), so the total time is at least 200ms. To run them in parallel, you would use await Promise.all([delay(100), delay(100)]), which would take only ~100ms.
</details>
Q40. What is the output?
A) Caught: Async Error Finished B) Finished then an uncaught error C) Finished Caught: Async Error D) Only Finished
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) Finished then an uncaught error
The try/catch block completes synchronously before the setTimeout callback executes. When the callback runs later, it's in a different execution context and the error is NOT caught by the original try/catch. It becomes an uncaught exception. To handle async errors, use try/catch inside the callback or use Promises with .catch().
</details>
Section 5: Advanced & Tricky Questions (Questions 41–50)
Q41. What is the output?
console.log([] + []);
console.log([] + {});
console.log({} + []);A) "" "[object Object]" "[object Object]" B) "" "[object Object]" 0 C) 0 NaN NaN D) "" "0" "0"
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) "" "[object Object]" 0
[] + []: Both arrays coerce to empty strings, concatenation gives "". [] + {}: Empty string + object's toString gives "[object Object]". {} + []: In many environments, {} is parsed as an empty block (not an object), so it becomes +[] which is 0. Note: behavior may vary if wrapped in parentheses or in different contexts.
</details>
Q42. What is the output?
const sym1 = Symbol("id");
const sym2 = Symbol("id");
console.log(sym1 === sym2);A) true B) false C) TypeError D) undefined
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) false
Every Symbol() call creates a unique, immutable symbol — even if the description string is the same. The description is just a label for debugging purposes. To create shared symbols, use Symbol.for("id") which uses a global symbol registry.
</details>
Q43. What is the output?
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() {
return `${this.name} says woof!`;
};
const dog = new Dog("Rex");
console.log(dog.bark());
console.log(dog.hasOwnProperty("bark"));A) "Rex says woof!" true B) "Rex says woof!" false C) undefined false D) TypeError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) "Rex says woof!" false
The bark method is defined on Dog.prototype, not directly on the instance. dog.bark() works because JavaScript looks up the prototype chain. However, hasOwnProperty("bark") returns false because bark is an inherited property, not an own property of the instance.
</details>
Q44. What is the output?
class Parent {
constructor() {
this.name = "Parent";
}
getName() {
return this.name;
}
}
class Child extends Parent {
constructor() {
super();
this.name = "Child";
}
}
const child = new Child();
console.log(child.getName());A) "Parent" B) "Child" C) undefined D) TypeError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) "Child"
super() calls the parent constructor which sets this.name = "Parent", but then the child constructor immediately overwrites it with this.name = "Child". When getName() is called, this.name is "Child". The method is inherited from the prototype chain but operates on the instance's current state.
</details>
Q45. What is the output?
A) "JavaScript" undefined B) "JavaScript" 'Property "age" not found' C) undefined undefined D) TypeError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) "JavaScript" 'Property "age" not found'
The Proxy intercepts property access with the get trap. When accessing proxy.name, the property exists in the target so it returns "JavaScript". When accessing proxy.age, it doesn't exist, so the custom message is returned. Proxies enable meta-programming and are used in frameworks like Vue 3.
</details>
Q46. What is the output?
A) 0 0 0 B) 0 1 2 C) 1 2 3 D) undefined undefined undefined
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 0 1 2
Generator functions (declared with function*) can be paused and resumed. Each yield pauses execution and returns the yielded value. i++ returns the current value of i then increments it. So we get 0, 1, 2 on successive next() calls.
</details>
Q47. What is the output?
const map = new WeakMap();
let obj = { key: "value" };
map.set(obj, "metadata");
console.log(map.get(obj));
obj = null;
console.log(map.get(obj));A) "metadata" "metadata" B) "metadata" undefined C) "metadata" null D) TypeError
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) "metadata" undefined
After obj = null, the original object reference is lost. map.get(null) returns undefined because null was never used as a key. Additionally, since there are no remaining references to the original object, the WeakMap entry becomes eligible for garbage collection. WeakMaps only accept objects as keys and don't prevent garbage collection.
</details>
Q48. What is the output?
const target = { a: 1, b: 2 };
const source = { b: 3, c: 4 };
const result = Object.assign(target, source);
console.log(target);
console.log(result === target);A) { a: 1, b: 2 } false B) { a: 1, b: 3, c: 4 } true C) { a: 1, b: 3, c: 4 } false D) { b: 3, c: 4 } true
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) { a: 1, b: 3, c: 4 } true
Object.assign() copies properties from source to target, overwriting existing properties with the same key. It mutates and returns the target object. So target is modified in place, and result is the same reference as target, hence result === target is true.
</details>
Q49. What is the output?
A) [1, 2, 3, 4, 5] B) [3, 4, 5] C) [2, 3, 4, 5] D) []
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) [3, 4, 5]
The iterator protocol allows consuming elements one at a time. After calling next() twice, the iterator has consumed values 1 and 2. Spreading the remaining iterator into an array gives [3, 4, 5]. This demonstrates how iterators maintain state between calls.
</details>
Q50. What is the output?
A) 8 8 B) 18 18 C) 8 18 D) 18 8
<details> <summary>✅ Answer & Explanation</summary>
Correct Answer: B) 18 18
The apply trap on a Proxy intercepts all function calls — both direct invocations and calls via .call(), .apply(), or .bind(). The trap adds 10 to the sum of arguments: 5 + 3 + 10 = 18. The original function is never actually executed because the trap handles the call entirely.
</details>
Summary & Tips for JavaScript MCQ Preparation
| Section | Topics Covered | Key Takeaways |
|---|---|---|
| Basics | Types, coercion, equality, typeof | Always use ===, understand type coercion rules |
| Functions & Scope | Hoisting, closures, this, arrow functions | Closures retain outer scope; arrow functions inherit this |
| Arrays & Objects | Destructuring, spread, methods, references | Know mutating vs non-mutating methods; objects compare by reference |
| Async | Event loop, Promises, async/await | Microtasks before macrotasks; sequential vs parallel awaits |
| Advanced | Proxies, generators, symbols, prototypes | Understand the prototype chain and iterator protocol |
Interview Tips
- Practice output-based questions — Most interviews test whether you can mentally execute code
- Understand the "why" — Don't just memorize answers; understand the underlying mechanisms
- Know the event loop — This is the #1 most asked async topic in interviews
- Master
thisbinding — Regular functions, arrow functions, and methods all behave differently - Learn ES6+ features — Destructuring, spread, generators, and Proxies are increasingly common in interviews
*Last updated: June 2026 | Created by WoHoTech*
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for JavaScript MCQs — 50 Multiple Choice Questions with Answers 2026.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this JavaScript Master Course topic.
Search Terms
javascript-complete-guide, javascript master course, javascript, complete, guide, practice, mcqs, javascript mcqs — 50 multiple choice questions with answers 2026
Related JavaScript Master Course Topics