Maria Ebrahimi
Maria Ebrahimi
  • Home
  • About
  • Experience
  • Services
  • Projects
  • Code Lab
  • Blog
Maria Ebrahimi
HomeAboutExperienceServicesProjectsCode LabBlog
// back to blog
JavaScript Interview QuestionsPART 1 OF 10

100 JavaScript Interview Questions — Part 1: Foundations

Questions 1 to 10 of the 100 JavaScript interview questions series: var/let/const, hoisting, closures, this, the event loop and other core fundamentals, with clear answers and runnable examples.

September 5, 20267 min readJavaScript, Interview, Fundamentals, Frontend
Glowing blue glass cubes stacked on a blueprint grid, representing JavaScript foundations
On this page
  • 1. What is the difference between var, let and const?
  • 2. What is hoisting?
  • 3. What is the difference between == and ===?
  • 4. What is a closure?
  • 5. How does the this keyword work?
  • 6. Explain the event loop
  • 7. What is the difference between null, undefined and undeclared?
  • 8. What is the prototype chain?
  • 9. How are arrow functions different from regular functions?
  • 10. What is scope and the scope chain?
On this page (10)
  • 1. What is the difference between var, let and const?
  • 2. What is hoisting?
  • 3. What is the difference between == and ===?
  • 4. What is a closure?
  • 5. How does the this keyword work?
  • 6. Explain the event loop
  • 7. What is the difference between null, undefined and undeclared?
  • 8. What is the prototype chain?
  • 9. How are arrow functions different from regular functions?
  • 10. What is scope and the scope chain?

1. What is the difference between var, let and const?

All three declare variables, but they differ in scope, hoisting behaviour and reassignment. var is function-scoped: it only respects function boundaries, so a var declared inside an if block leaks to the whole function. let and const are block-scoped and only exist inside the nearest pair of braces.

const additionally prevents reassignment of the binding itself. Note that it does not make objects immutable — you can still mutate the properties of a const object; you just cannot point the name at a different value.

A modern rule of thumb: use const by default, let when you need to reassign, and avoid var entirely.

example.js
1
2
3
4
5
6
7
8
9
10
11
12
if (true) {
  var a = 1;    // leaks outside the block
  let b = 2;    // block-scoped
  const c = 3;  // block-scoped, no reassignment
}

console.log(a); // 1
console.log(b); // ReferenceError: b is not defined

const user = { name: "Sara" };
user.name = "Ali";      // allowed — mutation, not reassignment
// user = {};           // TypeError — the binding is constant

2. What is hoisting?

Hoisting is the effect of JavaScript processing declarations before executing code. During the compilation phase, variable and function declarations are registered in their scope, so they appear to be moved to the top. Only declarations are hoisted — assignments are not.

var declarations are hoisted and initialized with undefined, which is why reading a var before its assignment gives undefined instead of an error. let and const are hoisted too, but they stay uninitialized until their declaration line is reached — this gap is called the Temporal Dead Zone (TDZ) and accessing them there throws a ReferenceError.

Function declarations are fully hoisted (definition included), so they can be called before they appear in the code. Function expressions assigned to a var behave like variables instead.

example.js
1
2
3
4
5
6
7
8
9
10
console.log(x); // undefined — var is hoisted with undefined
var x = 10;

console.log(y); // ReferenceError (TDZ)
let y = 20;

sayHi(); // works — function declarations are fully hoisted
function sayHi() {
  console.log("hi");
}
TipInterviewers love follow-ups here: be ready to explain why let/const throw while var returns undefined.

3. What is the difference between == and ===?

=== (strict equality) compares both type and value without any conversion. == (loose equality) first applies type coercion using a well-defined but famously confusing set of rules, then compares.

Classic traps: 0 == '' is true, null == undefined is true, but null == 0 is false. Because the coercion rules are hard to memorize, the recommended practice is to always use === and !==, with the only common exception being value == null to check for both null and undefined at once.

example.js
1
2
3
4
5
6
7
8
5 === "5"      // false — different types
5 == "5"       // true  — string is coerced to number

null == undefined   // true
null === undefined  // false

"" == 0        // true — an infamous trap
NaN === NaN    // false — NaN is never equal to itself

4. What is a closure?

A closure is a function that remembers the variables of the scope where it was created, even after that scope has finished executing. Functions in JavaScript carry their lexical environment with them.

Closures are the mechanism behind data privacy, factories, memoization and most callback patterns. Whenever an inner function reads or writes a variable from an outer function, that variable stays alive as long as the inner function exists.

example.js
1
2
3
4
5
6
7
8
9
10
11
12
function createCounter() {
  let count = 0; // private — not reachable from outside

  return function () {
    count += 1;
    return count;
  };
}

const next = createCounter();
console.log(next()); // 1
console.log(next()); // 2 — "count" survived between calls
TipIf asked for a real-world use, mention private state and function factories — counter is the canonical example.

5. How does the this keyword work?

this is not about where a function is defined — it is about how the function is called. There are four main rules: default binding (plain call, undefined in strict mode), implicit binding (object before the dot wins), explicit binding (call / apply / bind), and new binding (the freshly created object).

Arrow functions are the exception: they have no this of their own and always inherit it from the surrounding lexical scope. That makes them ideal for callbacks inside class methods and unsuitable as constructors.

example.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const user = {
  name: "Sara",
  greet() { console.log("Hi, " + this.name); },
};

user.greet();                  // implicit — this = user

const loose = user.greet;
loose();                       // default — this = undefined (strict)

const bound = user.greet.bind(user);
bound();                       // explicit — this = user

const arrow = () => this;      // lexical — inherits outer this

6. Explain the event loop

JavaScript runs on a single thread, but the runtime (browser or Node.js) provides Web APIs, queues and the event loop to handle work concurrently. Synchronous code executes line by line on the call stack.

When the stack becomes empty, the event loop checks the microtask queue (promise callbacks) and drains it completely, then takes one task from the macrotask queue (setTimeout, events, I/O). This is why promise callbacks always run before timers, even a setTimeout with a 0ms delay.

example.js
1
2
3
4
5
6
7
8
9
10
11
12
13
console.log("1");

setTimeout(function () {
  console.log("4"); // macrotask — runs last
}, 0);

Promise.resolve().then(function () {
  console.log("3"); // microtask — before timers
});

console.log("2");

// Output: 1, 2, 3, 4
TipThe expected output question (1, 2, 3, 4) is a favourite live-coding twist on this topic.

7. What is the difference between null, undefined and undeclared?

undefined means a variable exists but no value was assigned — fresh declarations, missing function arguments and absent object properties are all undefined. null is an intentional assignment of 'no value'; you set it yourself to mean empty.

Undeclared is different: the name was never declared in any scope, so reading it throws a ReferenceError. One historical quirk to know: typeof null returns 'object', which is a bug from the first JavaScript engine that was kept for compatibility.

example.js
1
2
3
4
5
6
7
8
9
10
let a;             // declared, never assigned
console.log(a);    // undefined

let b = null;      // deliberately empty
console.log(b);    // null

console.log(c);    // ReferenceError — never declared

typeof null;       // "object" (the famous bug)
typeof undefined;  // "undefined"

8. What is the prototype chain?

Every JavaScript object has an internal link to another object, its prototype. When you read a property, the engine first looks at the object itself; if it is not there, it walks up the chain — prototype, then its prototype — until it finds the property or reaches null.

This is how inheritance works in JavaScript. Arrays find methods like map on Array.prototype, and everything ultimately inherits from Object.prototype. Classes introduced in ES6 are syntax sugar over exactly this mechanism.

example.js
1
2
3
4
5
6
7
8
9
10
11
const animal = {
  eats: true,
  describe() { return "eats: " + this.eats; },
};

const rabbit = Object.create(animal); // rabbit -> animal
rabbit.hops = true;

console.log(rabbit.hops);    // own property
console.log(rabbit.eats);    // found on the prototype
console.log(rabbit.describe()); // inherited method

9. How are arrow functions different from regular functions?

Arrow functions are shorter to write, but the real differences are semantic: they have no `this` of their own (they inherit it lexically), no arguments object, and they cannot be used as constructors — calling them with new throws. They also cannot be used as generators.

Because of the lexical this, arrows are perfect for callbacks inside methods and array helpers, but a poor choice for object methods and event handlers that rely on dynamic this.

example.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const nums = [1, 2, 3];

// concise body with implicit return
const doubled = nums.map(function (n) { return n * 2; });
const doubledArrow = nums.map(n => n * 2);

const Arrow = () => {};
// new Arrow(); // TypeError — arrows are not constructors

const obj = {
  value: 1,
  regular() { return this.value; },
  arrow: () => this.value, // inherits outer this!
};

obj.regular(); // 1
obj.arrow();   // undefined — not obj

10. What is scope and the scope chain?

Scope is the region of code where a variable is visible. JavaScript has global scope, function scope (created by functions) and block scope (created by {} with let/const). Scopes nest, forming the scope chain.

When code reads a variable, the engine searches the current scope, then the enclosing one, and so on up to the global scope. If nothing is found, it throws a ReferenceError. Writing creates the variable in the nearest scope — or implicitly on the global object if you assign to an undeclared name in sloppy mode, which is exactly why strict mode exists.

example.js
1
2
3
4
5
6
7
8
9
10
11
12
13
const theme = "dark"; // global

function render() {
  const theme = "light"; // function scope — shadows global

  function button() {
    console.log(theme); // walks up: finds "light"
  }

  button();
}

render(); // "light"
TipConnect this answer to closures: a closure is simply a function using variables found via the scope chain.
// all parts of 100 JavaScript Interview Questions
Next · Part 2

100 JavaScript Interview Questions — Part 2: Async & Modern Syntax

Keep reading

Ready-to-Use AI Prompts for Front-End Developers

AI Prompts

Ready-to-Use AI Prompts for Front-End Developers

Ten battle-tested AI prompt templates for front-end work: component generation, code review, CSS debugging, refactoring, accessibility, performance, TypeScript and more. Copy, fill the placeholders, paste.

September 14, 20265 min read
Glowing blue light curves weaving through connected nodes, representing asynchronous JavaScript
JavaScript Interview QuestionsPART 2

100 JavaScript Interview Questions — Part 2: Async & Modern Syntax

Questions 11 to 20 of the 100 JavaScript interview questions series: callbacks, promises, async/await, promise combinators, destructuring, spread/rest, currying, map/filter/reduce, memoization and copying objects.

September 12, 20267 min read
© 2026 Maria Ebrahimi— Designed & built with care. · Become a writer · Privacy Policy