[{"data":1,"prerenderedAt":328},["ShallowReactive",2],{"topic-javascript-fundamentals":3},{"framework":4,"topic":15,"subtopics":23},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fjavascript.yml","Core JavaScript interview questions covering fundamentals, functions and closures, objects and prototypes, classes and OOP, and asynchronous programming with the event loop.","yml","javascript",{},"JavaScript",1,"frameworks\u002Fjavascript",0,"_TjxyYWBq7dftU9YakX_WX1Z4wAHq9uEUUW7wXL0y0c",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":11,"slug":20,"stem":21,"__hash__":22},"topics\u002Ftopics\u002Fjavascript-fundamentals.yml","Variables, scope, hoisting, data types and type coercion — the bedrock concepts every JavaScript interview starts with.",{},"Fundamentals","fundamentals","topics\u002Fjavascript-fundamentals","iD2M8X27iFyEzm-j-SDyMRdEhVFmIAx80cJzIDxm310",[24,181],{"id":25,"title":26,"body":27,"description":31,"difficulty":34,"extension":35,"framework":10,"frameworkSlug":8,"meta":36,"navigation":37,"order":11,"path":38,"questions":39,"related":174,"seo":175,"seoDescription":176,"stem":177,"subtopic":178,"topic":19,"topicSlug":20,"updated":179,"__hash__":180},"qa\u002Fjavascript\u002Ffundamentals\u002Fvariables-scope-hoisting.md","Variables Scope Hoisting",{"type":28,"value":29,"toc":30},"minimark",[],{"title":31,"searchDepth":32,"depth":32,"links":33},"",2,[],"easy","md",{},true,"\u002Fjavascript\u002Ffundamentals\u002Fvariables-scope-hoisting",[40,44,49,53,57,61,65,70,74,78,82,86,90,94,98,102,106,110,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170],{"id":41,"difficulty":34,"q":42,"a":43},"var-let-const","What is the difference between var, let and const?","They differ along three axes: **scope**, **hoisting\u002Finitialization**, and\n**reassignment**.\n\n| Keyword | Scope    | Hoisted? | Reassignable? | Redeclarable? |\n| ------- | -------- | -------- | ------------- | ------------- |\n| `var`   | function | yes, init `undefined` | yes | yes |\n| `let`   | block    | yes, in TDZ (uninit)  | yes | no  |\n| `const` | block    | yes, in TDZ (uninit)  | no  | no  |\n\nThe subtle one is `const`: it makes the **binding** constant, **not the\nvalue**. You can't reassign the variable, but if it holds an object or array\nyou can still mutate the contents.\n\n```js\nconst user = { name: 'Ada' }\nuser.name = 'Grace' \u002F\u002F OK — mutating the object the binding points to\nuser = {}           \u002F\u002F TypeError — reassigning the binding\n\nconst list = [1, 2]\nlist.push(3)        \u002F\u002F [1, 2, 3]\n```\n\nModern guidance: default to `const`, use `let` when you genuinely need to\nreassign, and avoid `var`.\n",{"id":45,"difficulty":46,"q":47,"a":48},"hoisting","medium","What is hoisting?","Hoisting is the JavaScript engine processing **declarations** before executing\ncode, so they behave as if \"moved\" to the top of their scope. But *what* gets\nhoisted differs:\n\n- `var` declarations are hoisted **and initialized to `undefined`**, so reading\n  one before its assignment gives `undefined` (no error).\n- **Function declarations** are hoisted **entirely** — you can call them above\n  where they're written.\n- `let`\u002F`const` are hoisted **but left uninitialized** in the *temporal dead\n  zone*; touching them early throws.\n\n```js\nconsole.log(a) \u002F\u002F undefined  (var hoisted, value not yet assigned)\nvar a = 1\n\ngreet()        \u002F\u002F 'hi'  (function declaration fully hoisted)\nfunction greet() { return 'hi' }\n\nconsole.log(b) \u002F\u002F ReferenceError (TDZ)\nlet b = 2\n```\n\nMental model: declarations are \"registered\" first; only the **assignments**\nrun in place.\n",{"id":50,"difficulty":46,"q":51,"a":52},"tdz","What is the temporal dead zone (TDZ)?","The TDZ is the window between **entering a scope** and the line where a\n`let`\u002F`const` variable is actually **declared**. The binding exists (it was\nhoisted) but is *uninitialized*, so any attempt to read or write it in that\nwindow throws a `ReferenceError`.\n\n```js\n{\n  \u002F\u002F TDZ for `x` starts here\n  console.log(x) \u002F\u002F ReferenceError: Cannot access 'x' before initialization\n  let x = 5      \u002F\u002F TDZ ends — x is now initialized\n  console.log(x) \u002F\u002F 5\n}\n```\n\nIt exists on purpose: it turns \"used before declared\" from a silent\n`undefined` bug (the `var` behavior) into a **loud, immediate error**, which\ncatches mistakes earlier and makes `const` semantics sound.\n",{"id":54,"difficulty":34,"q":55,"a":56},"block-scope","What is the difference between block scope and function scope?","**Function scope** (`var`) means a variable is visible **anywhere inside the\nenclosing function**, regardless of which `{ }` blocks it's nested in. **Block\nscope** (`let`\u002F`const`) confines a variable to the **nearest pair of braces** —\nincluding `if`, `for`, `while`, and even a bare `{ }` block.\n\n```js\nfunction demo() {\n  if (true) {\n    var v = 'function-scoped'\n    let l = 'block-scoped'\n  }\n  console.log(v) \u002F\u002F 'function-scoped' — var leaks out of the if\n  console.log(l) \u002F\u002F ReferenceError — l only exists inside the if\n}\n```\n\nBlock scoping is generally safer: variables stay where they're relevant and\nyou avoid accidental leakage across a function — which is one big reason `let`\u002F\n`const` replaced `var`.\n",{"id":58,"difficulty":34,"q":59,"a":60},"redeclare","Can you redeclare variables in the same scope?","`var` lets you **redeclare** the same name in the same scope without\ncomplaint — which silently masks bugs and accidental overwrites. `let` and\n`const` **throw a `SyntaxError`** if you redeclare an identifier in the same\nscope.\n\n```js\nvar x = 1\nvar x = 2      \u002F\u002F allowed (and easy to do by accident)\n\nlet y = 1\nlet y = 2      \u002F\u002F SyntaxError: Identifier 'y' has already been declared\n```\n\nNote this is about *redeclaration in the same scope*. You can still\n**shadow** a `let` in a nested inner block with a new `let` of the same name —\nthat creates a separate binding and is perfectly legal.\n",{"id":62,"difficulty":46,"q":63,"a":64},"global-this","Do var, let and const create properties on the global object?","At the **top level** of a script, a `var` declaration *also* creates a property\non the global object (`window` in browsers, `globalThis` generally). `let` and\n`const` do **not** — they create bindings in a separate *script-scoped* record\nthat the global object can't see.\n\n```js\nvar a = 1\nlet b = 2\nconst c = 3\n\nwindow.a \u002F\u002F 1          \u003C- var leaked onto the global object\nwindow.b \u002F\u002F undefined  \u003C- let did not\nwindow.c \u002F\u002F undefined\n```\n\nThis is another reason to avoid top-level `var`: it pollutes the global\nnamespace and can clash with other scripts or built-ins. (Inside ES modules,\neven top-level `var` doesn't attach to the global object, since module scope\nisn't global scope.)\n",{"id":66,"difficulty":67,"q":68,"a":69},"loop-closure","hard","Why does var behave unexpectedly in a for loop with closures?","Because `var` is **function-scoped**, the loop reuses **one single binding**\nacross all iterations. Any callbacks created in the loop close over that *same*\nvariable, so when they run later they all read its **final value**. `let`\ncreates a **fresh binding each iteration**, so each closure captures its own\ncopy.\n\n```js\nfor (var i = 0; i \u003C 3; i++) setTimeout(() => console.log(i)) \u002F\u002F 3 3 3\nfor (let j = 0; j \u003C 3; j++) setTimeout(() => console.log(j)) \u002F\u002F 0 1 2\n```\n\nWith `var`, by the time the timers fire the loop has long finished and `i` is\n`3`. With `let`, the language re-binds `j` per iteration (and even copies the\nvalue forward for the increment), giving each callback `0`, `1`, `2`. The\npre-`let` fix was an IIFE to manufacture a new scope each pass.\n",{"id":71,"difficulty":34,"q":72,"a":73},"const-array","Can you modify an array declared with const?","Yes. `const` freezes the **binding**, not the **value**. The variable must keep\npointing at the same array, but the array's contents are fully mutable.\n\n```js\nconst arr = [1, 2, 3]\narr.push(4)      \u002F\u002F [1, 2, 3, 4]\narr[0] = 99      \u002F\u002F [99, 2, 3, 4]\narr.length = 0   \u002F\u002F [] — still the same array\narr = []         \u002F\u002F TypeError: Assignment to constant variable\n```\n\nIf you want the *contents* to be immutable too, that's a separate concern —\nuse `Object.freeze(arr)` for a shallow freeze (and note `freeze` is shallow, so\nnested objects can still change).\n",{"id":75,"difficulty":46,"q":76,"a":77},"lexical-scope","What is lexical scope?","Lexical (static) scope means a variable's accessibility is determined by **where\nit's written** in the source code — the nesting of functions\u002Fblocks — not by where\nor how a function is called. The structure is fixed at author time.\n\n```js\nconst outer = 1\nfunction f() {\n  const inner = 2\n  function g() { return outer + inner } \u002F\u002F sees both by lexical nesting\n  return g()\n}\nf() \u002F\u002F 3\n```\n\nBecause scope is determined by position, you can tell what any function can access\njust by reading the code. JavaScript uses lexical scope (unlike languages with\ndynamic scope).\n",{"id":79,"difficulty":46,"q":80,"a":81},"scope-chain","What is the scope chain?","When you reference a variable, the engine looks in the **current scope**, then its\n**enclosing scope**, and so on outward until the global scope — this series of\nlinked scopes is the scope chain. The first match wins; if none is found, it's a\n`ReferenceError`.\n\n```js\nconst a = 'global'\nfunction outer() {\n  const b = 'outer'\n  function inner() {\n    const c = 'inner'\n    return `${a} ${b} ${c}` \u002F\u002F resolves c->inner, b->outer, a->global\n  }\n  return inner()\n}\n```\n\nLookups go **inward-to-outward only** — an outer scope can't see inner variables.\nThis chain is also what closures preserve.\n",{"id":83,"difficulty":46,"q":84,"a":85},"shadowing","What is variable shadowing?","Shadowing is declaring a variable in an inner scope with the **same name** as one\nin an outer scope. The inner variable \"shadows\" the outer one within that scope;\nthe outer remains unchanged outside.\n\n```js\nconst x = 1\nfunction f() {\n  const x = 2   \u002F\u002F shadows the outer x\n  console.log(x) \u002F\u002F 2\n}\nf()\nconsole.log(x)  \u002F\u002F 1 (outer untouched)\n```\n\nShadowing is legal and sometimes useful, but accidental shadowing causes bugs.\nNote you **can't** shadow with `var`\u002F`let` in a way that accesses the outer value\nmid-declaration (TDZ), and linters can warn on shadowing.\n",{"id":87,"difficulty":67,"q":88,"a":89},"fn-decl-vs-block","How are function declarations scoped inside blocks?","In **strict mode** (the default in modules\u002Fclasses), a function declaration inside\na block is **block-scoped** — visible only within that block. In sloppy mode the\nbehavior is inconsistent across engines, which is a common gotcha.\n\n```js\n'use strict'\nif (true) {\n  function foo() { return 1 }\n}\ntypeof foo \u002F\u002F 'undefined' in strict mode — block-scoped\n```\n\nBest practice: don't declare functions inside blocks. Use a **function\nexpression** assigned to a `let`\u002F`const` if you need a conditionally-defined\nfunction, for predictable scoping.\n",{"id":91,"difficulty":67,"q":92,"a":93},"let-switch","Why can let and const be tricky in a switch statement?","A `switch` has **one shared block scope** across all its `case`s. Declaring\n`let`\u002F`const` in one case puts it in the TDZ for the entire switch, so another case\ncan hit a `ReferenceError` or a redeclaration `SyntaxError`.\n\n```js\nswitch (x) {\n  case 1:\n    let y = 'a'   \u002F\u002F y is scoped to the whole switch\n    break\n  case 2:\n    let y = 'b'   \u002F\u002F SyntaxError: 'y' already declared\n    break\n}\n```\n\nFix by wrapping each case body in its own **block** `{ ... }`, giving each `case`\na separate scope.\n",{"id":95,"difficulty":46,"q":96,"a":97},"fn-expr-hoist","Are function expressions hoisted?","The **variable** is hoisted, but the **function value is not** — only the\ndeclaration is. So calling a function expression before its assignment fails\n(`undefined`\u002FTDZ), unlike a function declaration which is fully hoisted.\n\n```js\ndeclared()  \u002F\u002F works — declaration hoisted\nfunction declared() {}\n\nexpressed() \u002F\u002F TypeError: expressed is not a function (var) \u002F ReferenceError (let)\nvar expressed = function () {}\n```\n\nSo function **declarations** are callable above their line; function\n**expressions** (including arrows) follow normal variable hoisting rules and\naren't.\n",{"id":99,"difficulty":67,"q":100,"a":101},"class-hoist","Are class declarations hoisted?","Class declarations **are hoisted** but, like `let`\u002F`const`, remain in the\n**temporal dead zone** — so you **can't use a class before its declaration**.\nUnlike function declarations, they aren't callable early.\n\n```js\nnew Foo()  \u002F\u002F ReferenceError: Cannot access 'Foo' before initialization\nclass Foo {}\n\nbar()      \u002F\u002F function declarations work\nfunction bar() {}\n```\n\nSo \"classes are hoisted\" is technically true, but the TDZ means it behaves like\nnot-hoisted in practice. Always declare classes before using them.\n",{"id":103,"difficulty":67,"q":104,"a":105},"named-fn-expr","What is a named function expression and why use one?","A named function expression gives the function a name that's **only visible inside\nits own body** (not the outer scope). It's useful for self-reference (recursion)\nand clearer stack traces.\n\n```js\nconst factorial = function fact(n) {\n  return n \u003C= 1 ? 1 : n * fact(n - 1) \u002F\u002F `fact` usable inside\n}\nfactorial(5) \u002F\u002F 120\nfact          \u002F\u002F ReferenceError — not visible outside\n```\n\nThe inner name is safer than relying on the outer variable (which could be\nreassigned) and shows up in debuggers instead of \"anonymous.\"\n",{"id":107,"difficulty":34,"q":108,"a":109},"nested-scope-lookup","How does variable lookup work in nested functions?","An inner function can read variables from all of its enclosing scopes, resolved\nvia the scope chain. Each function adds a scope; lookups proceed outward until a\nmatch or the global scope.\n\n```js\nfunction a() {\n  let x = 1\n  function b() {\n    let y = 2\n    function c() { return x + y } \u002F\u002F reaches up two levels\n    return c()\n  }\n  return b()\n}\na() \u002F\u002F 3\n```\n\nInner scopes see outer variables, never the reverse. This nested visibility is the\nfoundation of closures.\n",{"id":111,"difficulty":67,"q":112,"a":113},"implicit-global","What is an accidental (implicit) global variable?","Assigning to a variable **without declaring** it (`x = 5`) in sloppy mode creates\na property on the **global object** instead of erroring — a common source of bugs\nand leaks. **Strict mode** throws a `ReferenceError` instead.\n\n```js\nfunction f() {\n  count = 1   \u002F\u002F no var\u002Flet\u002Fconst -> implicit global\n}\nf()\nconsole.log(count) \u002F\u002F 1 (leaked to global!)\n\n\u002F\u002F strict mode:\n'use strict'\nfunction g() { total = 1 } \u002F\u002F ReferenceError\n```\n\nAlways declare variables, and use strict mode (automatic in modules) so this\nmistake fails loudly.\n",{"id":115,"difficulty":46,"q":116,"a":117},"strict-mode-vars","How does strict mode change variable behavior?","Strict mode tightens several variable rules: assigning to an undeclared variable\n**throws** (no accidental globals), you can't `delete` a variable, duplicate\nparameter names are errors, and `this` is `undefined` instead of the global object\nin plain calls.\n\n```js\n'use strict'\nundeclared = 5   \u002F\u002F ReferenceError\nfunction dup(a, a) {} \u002F\u002F SyntaxError\n```\n\nES modules and class bodies are **always** strict. Strict mode surfaces bugs at\nauthor\u002Fparse time that would otherwise fail silently — a big reason modern code is\nstrict by default.\n",{"id":119,"difficulty":34,"q":120,"a":121},"const-init","Why must a const be initialized at declaration?","Because `const` can be assigned **only once**, you must give it that value at\ndeclaration — there's no later opportunity to assign. Declaring without a value is\na `SyntaxError`.\n\n```js\nconst x = 5   \u002F\u002F\nconst y        \u002F\u002F SyntaxError: Missing initializer in const declaration\n```\n\n`let` and `var` can be declared without a value (defaulting to `undefined`), but\n`const`'s \"assign once\" contract requires the value up front. This also makes\n`const` ineligible for the classic split declare-then-assign pattern.\n",{"id":123,"difficulty":67,"q":124,"a":125},"var-fn-clash","What happens when a var and a function declaration share a name?","During hoisting, **function declarations take precedence** over `var`\ndeclarations with the same name. The `var` declaration is ignored (its\nassignment, if any, still runs in place and can overwrite the function later).\n\n```js\nconsole.log(typeof foo) \u002F\u002F 'function' — function decl wins at hoist time\nvar foo = 'bar'\nfunction foo() {}\nconsole.log(typeof foo) \u002F\u002F 'string' — the assignment ran\n```\n\nSo at the top the name is the function; once execution reaches `foo = 'bar'`, it\nbecomes the string. These clashes are confusing — avoid reusing names.\n",{"id":127,"difficulty":46,"q":128,"a":129},"module-scope","What is module scope?","Each ES module has its own **top-level scope** — variables declared at the top of\na module are **not** global; they're private to that module unless `export`ed.\nEven top-level `var` doesn't attach to the global object.\n\n```js\n\u002F\u002F module.js\nconst secret = 42       \u002F\u002F module-scoped, not global\nexport const api = {}   \u002F\u002F shared only via export\n\u002F\u002F window.secret -> undefined\n```\n\nThis is a major improvement over classic scripts, where top-level `var` polluted\nthe global namespace. Module scope plus strict mode is why modern code avoids many\nlegacy footguns.\n",{"id":131,"difficulty":67,"q":132,"a":133},"let-per-iteration","How does let create a new binding each loop iteration?","In a `for` loop, `let` creates a **fresh binding per iteration** and copies the\nvalue forward — so closures created in the loop each capture their own copy. `var`\nshares one binding across all iterations.\n\n```js\nconst fns = []\nfor (let i = 0; i \u003C 3; i++) fns.push(() => i)\nfns.map(f => f())  \u002F\u002F [0, 1, 2]\n\nconst v = []\nfor (var j = 0; j \u003C 3; j++) v.push(() => j)\nv.map(f => f())    \u002F\u002F [3, 3, 3]\n```\n\nThis per-iteration binding is a deliberate `let` feature that fixes the\nlong-standing closure-in-loop bug.\n",{"id":135,"difficulty":67,"q":136,"a":137},"tdz-typeof","Does typeof protect against the temporal dead zone?","No. `typeof` is safe for **undeclared** identifiers, but a `let`\u002F`const` variable\nin its **TDZ** is declared-but-uninitialized, so even `typeof` throws a\n`ReferenceError`.\n\n```js\ntypeof undeclaredVar  \u002F\u002F 'undefined' (safe)\n\ntypeof x              \u002F\u002F ReferenceError (x is in the TDZ)\nlet x = 1\n```\n\nSo the usual \"use `typeof` to safely check existence\" trick fails for block-scoped\nvariables before their declaration line. The TDZ deliberately makes\nuse-before-declaration an error.\n",{"id":139,"difficulty":46,"q":140,"a":141},"bare-block","What is a bare block and how does it scope variables?","A bare block is a standalone `{ ... }` (not attached to `if`\u002F`for`\u002Fetc.). With\n`let`\u002F`const` it creates a **new block scope**, so variables inside are invisible\noutside — a lightweight way to limit scope. `var` ignores it (function-scoped).\n\n```js\n{\n  let secret = 42\n  var leaked = 1\n}\nconsole.log(leaked) \u002F\u002F 1 (var ignores the block)\nconsole.log(secret) \u002F\u002F ReferenceError (let is block-scoped)\n```\n\nBare blocks can also resolve `switch`\u002Ftemporary-variable naming conflicts by\nisolating declarations.\n",{"id":143,"difficulty":46,"q":144,"a":145},"shadow-nested","Can you shadow a let variable in a nested block?","Yes. A nested block can declare a new `let`\u002F`const` with the same name, creating a\n**separate binding** that shadows the outer one within that block. This is\ndifferent from redeclaration in the **same** scope (which errors).\n\n```js\nlet x = 1\n{\n  let x = 2     \u002F\u002F shadows — different (nested) scope\n  console.log(x) \u002F\u002F 2\n}\nconsole.log(x)  \u002F\u002F 1\n\nlet y = 1\nlet y = 2       \u002F\u002F SyntaxError — same scope\n```\n\nShadowing across scopes is allowed; redeclaring in the same scope is not.\n",{"id":147,"difficulty":67,"q":148,"a":149},"dynamic-vs-lexical","What is the difference between lexical and dynamic scoping?","- **Lexical scoping** (what JS uses): a function's free variables resolve based on\n  **where it's defined** in the source.\n- **Dynamic scoping** (some other languages): free variables resolve based on the\n  **call stack at runtime** — who called the function.\n\n```js\nlet x = 'global'\nfunction inner() { return x }\nfunction outer() { let x = 'outer'; return inner() }\nouter() \u002F\u002F 'global' — lexical: inner sees where it was DEFINED\n\u002F\u002F (a dynamically-scoped language would return 'outer')\n```\n\nJS's only \"dynamic\" feature is `this`, which is bound by the call site — but\nordinary variable lookup is always lexical.\n",{"id":151,"difficulty":46,"q":152,"a":153},"hoisting-mechanism","What actually happens during hoisting (the two phases)?","JavaScript runs in two phases per scope: a **creation (compilation) phase** that\nregisters all declarations (allocating `var` as `undefined`, `let`\u002F`const` as\nuninitialized in the TDZ, functions fully), and an **execution phase** that runs\nthe code and performs assignments in order.\n\n```js\n\u002F\u002F creation phase registers: a (undefined), greet (function)\nconsole.log(a)  \u002F\u002F undefined\nvar a = 1       \u002F\u002F execution: a becomes 1\ngreet()         \u002F\u002F already callable\nfunction greet() {}\n```\n\nSo \"hoisting\" isn't physical code movement — it's declarations being processed\nbefore any line executes. Only assignments happen in place.\n",{"id":155,"difficulty":46,"q":156,"a":157},"var-leak","Why is var leaking out of blocks a problem?","`var` is function-scoped, so a `var` declared inside an `if`\u002F`for`\u002Fblock is visible\nthroughout the **entire enclosing function** — leaking beyond where it's relevant,\nwhich causes accidental reuse and bugs.\n\n```js\nfunction f() {\n  for (var i = 0; i \u003C 3; i++) {}\n  console.log(i) \u002F\u002F 3 — i leaked out of the loop\n\n  if (true) { var temp = 'x' }\n  console.log(temp) \u002F\u002F 'x' — leaked out of the if\n}\n```\n\n`let`\u002F`const` confine variables to their block, keeping scope tight. This leakage\nis a primary reason to avoid `var`.\n",{"id":159,"difficulty":67,"q":160,"a":161},"deep-freeze","How do you make an object deeply immutable?","`Object.freeze` is **shallow** — it freezes the top-level properties but nested\nobjects remain mutable. For deep immutability you must recursively freeze.\n\n```js\nconst obj = Object.freeze({ a: 1, nested: { b: 2 } })\nobj.a = 9          \u002F\u002F ignored (or throws in strict mode)\nobj.nested.b = 99  \u002F\u002F still mutable! freeze is shallow\n\nfunction deepFreeze(o) {\n  Object.values(o).forEach(v => v && typeof v === 'object' && deepFreeze(v))\n  return Object.freeze(o)\n}\n```\n\nCheck frozen status with `Object.isFrozen`. For shared constants, deep-freezing\nprevents accidental mutation.\n",{"id":163,"difficulty":46,"q":164,"a":165},"iife-scope-isolation","How does an IIFE isolate scope?","An IIFE runs immediately and creates a **private function scope**, so variables\ninside never touch the enclosing\u002Fglobal scope. Before block scoping and modules, it\nwas the main tool to avoid polluting globals and to create private state.\n\n```js\n(function () {\n  var temp = 'private'   \u002F\u002F not global\n  \u002F\u002F setup work...\n})()\ntypeof temp \u002F\u002F 'undefined' — isolated\n```\n\nToday, block scope (`{ let ... }`) and ES modules cover most of these needs, but\nIIFEs still appear in bundled code and for one-off encapsulation.\n",{"id":167,"difficulty":34,"q":168,"a":169},"let-vs-const-when","When should you use let vs const?","Default to **`const`** — it signals the binding won't be reassigned, preventing\naccidental reassignment and making code easier to reason about. Use **`let`** only\nwhen you genuinely need to reassign (counters, accumulators, reassigned in\nbranches). Avoid `var`.\n\n```js\nconst MAX = 100          \u002F\u002F never reassigned\nconst user = { id: 1 }   \u002F\u002F const, but you can still mutate the object\nlet total = 0            \u002F\u002F reassigned in a loop\nfor (const item of items) total += item.price\n```\n\n\"const by default, let when needed\" is the widely-adopted convention. Remember\n`const` blocks reassignment, not mutation.\n",{"id":171,"difficulty":46,"q":172,"a":173},"global-pollution","Why are global variables discouraged?","Globals are accessible and mutable from **anywhere**, so they create hidden\ncoupling, name collisions between scripts\u002Flibraries, hard-to-trace bugs, and\nproblems in concurrent\u002Ftesting contexts. Any code can change them unexpectedly.\n\n```js\n\u002F\u002F global state anyone can clobber\nvar config = {}\n\u002F\u002F encapsulate in a module or closure\nexport const config = createConfig()\n```\n\nMinimize globals by using **modules** (module scope), closures, and passing\ndependencies explicitly. When a true global is needed, namespace it under a single\nobject to limit collisions.\n",null,{"description":31},"Common JavaScript interview questions on var, let and const, scope, hoisting and the temporal dead zone, with clear answers and examples.","javascript\u002Ffundamentals\u002Fvariables-scope-hoisting","Variables, Scope & Hoisting","2026-06-17","1OhvPeamj0K5ICmSSBRUFJ9iY7c4aoSeIv0ARSpSdac",{"id":182,"title":183,"body":184,"description":31,"difficulty":46,"extension":35,"framework":10,"frameworkSlug":8,"meta":188,"navigation":37,"order":32,"path":189,"questions":190,"related":174,"seo":323,"seoDescription":324,"stem":325,"subtopic":326,"topic":19,"topicSlug":20,"updated":179,"__hash__":327},"qa\u002Fjavascript\u002Ffundamentals\u002Fdata-types-coercion.md","Data Types Coercion",{"type":28,"value":185,"toc":186},[],{"title":31,"searchDepth":32,"depth":32,"links":187},[],{},"\u002Fjavascript\u002Ffundamentals\u002Fdata-types-coercion",[191,195,199,203,207,211,215,219,223,227,231,235,239,243,247,251,255,259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319],{"id":192,"difficulty":34,"q":193,"a":194},"primitives","What are the primitive types in JavaScript?","There are **seven primitive types**: `string`, `number`, `boolean`, `null`,\n`undefined`, `symbol`, and `bigint`. Everything that isn't a primitive is an\n**object** — and that includes arrays, functions, dates, and `null`'s\nmisleading `typeof`.\n\n```js\ntypeof 'hi'        \u002F\u002F 'string'\ntypeof 42          \u002F\u002F 'number'\ntypeof true        \u002F\u002F 'boolean'\ntypeof undefined   \u002F\u002F 'undefined'\ntypeof Symbol()    \u002F\u002F 'symbol'\ntypeof 10n         \u002F\u002F 'bigint'\ntypeof null        \u002F\u002F 'object'  \u003C- historical bug, null IS a primitive\n```\n\nTwo defining traits: primitives are **immutable** (you can't change the value\nitself, only rebind the variable) and they're **compared by value**, whereas\nobjects are mutable and **compared by reference**. When you call a method like\n`'hi'.toUpperCase()`, JS temporarily wraps the primitive in an object\n(autoboxing) and discards it afterward.\n",{"id":196,"difficulty":34,"q":197,"a":198},"eqeq-vs-eqeqeq","What is the difference between == and ===?","`===` is **strict equality**: it compares **value *and* type** with no\nconversion — if the types differ, it's immediately `false`. `==` is **loose\nequality**: it **coerces** the operands toward a common type first, which\nproduces a list of surprising results.\n\n```js\n0 === ''      \u002F\u002F false (number vs string)\n0 == ''       \u002F\u002F true  (both coerce to 0)\n0 == '0'      \u002F\u002F true\n'' == '0'     \u002F\u002F false  \u003C- not even transitive!\nnull == undefined \u002F\u002F true (special-cased)\nNaN == NaN    \u002F\u002F false\n[] == ![]     \u002F\u002F true  (classic \"wtf\" — [] coerces to '', ![] is false -> 0)\n```\n\n**Prefer `===` virtually always** — it's predictable and the coercion rules\nbehind `==` are a frequent bug source. The one common idiomatic exception is\n`x == null`, which conveniently matches *both* `null` and `undefined`.\n",{"id":200,"difficulty":46,"q":201,"a":202},"coercion","What is type coercion?","Coercion is JavaScript **automatically converting** a value from one type to\nanother. It comes in two flavors: **implicit** (the engine does it for you\nduring an operation, e.g. `'5' * 2`) and **explicit** (you ask for it,\n`Number('5')`, `String(5)`, `Boolean(0)`).\n\nThe operator that trips everyone up is `+`: if **either** side is a string, it\ndoes **string concatenation**; otherwise it does numeric addition. The other\narithmetic operators (`-`, `*`, `\u002F`) have no string meaning, so they coerce to\nnumbers.\n\n```js\n1 + '2'    \u002F\u002F '12'   (+ with a string -> concatenation)\n1 - '2'    \u002F\u002F -1     (- forces numeric -> 1 - 2)\n'5' * '2'  \u002F\u002F 10     (* forces numeric)\ntrue + 1   \u002F\u002F 2      (true -> 1)\n[] + {}    \u002F\u002F '[object Object]'  (both -> strings)\n```\n\nIn an interview, narrate the rule (\"`+` prefers strings, the rest prefer\nnumbers\") rather than memorizing every edge case.\n",{"id":204,"difficulty":34,"q":205,"a":206},"truthy-falsy","Which values are falsy in JavaScript?","There are exactly **eight falsy values** — memorize the list, because\n*everything else is truthy*:\n\n```js\nfalse\n0\n-0\n0n        \u002F\u002F BigInt zero\n''        \u002F\u002F empty string\nnull\nundefined\nNaN\n```\n\nThe famous gotchas are the things people *expect* to be falsy but aren't:\n`'0'`, `'false'`, `[]` (empty array), and `{}` (empty object) are **all\ntruthy**.\n\n```js\nif ([]) console.log('runs!')   \u002F\u002F empty array is truthy\nif ('0') console.log('runs!')  \u002F\u002F non-empty string is truthy\nBoolean([]) \u002F\u002F true\n```\n\nThis matters for `if` conditions, `||`\u002F`&&`, and the nullish coalescing\noperator `??` (which, unlike `||`, only falls back on `null`\u002F`undefined`, not\non `0` or `''`).\n",{"id":208,"difficulty":46,"q":209,"a":210},"null-undefined","What is the difference between null and undefined?","Both represent \"no value,\" but they signal different *intents*:\n\n- `undefined` is the language's default \"absence\" — a declared-but-unassigned\n  variable, a missing object property, a missing function argument, or the\n  return of a function with no `return`. The **engine** produces it.\n- `null` is an **intentional, explicit** \"no value here\" that **you** assign to\n  say \"this is deliberately empty.\"\n\n```js\nlet a               \u002F\u002F undefined (never assigned)\nconst obj = {}\nobj.missing         \u002F\u002F undefined (no such property)\nconst b = null      \u002F\u002F null (you chose emptiness)\n\ntypeof undefined    \u002F\u002F 'undefined'\ntypeof null         \u002F\u002F 'object'  \u003C- long-standing bug, never fixed for compat\nnull == undefined   \u002F\u002F true  (loose — they're \"equal absences\")\nnull === undefined  \u002F\u002F false (different types)\n```\n",{"id":212,"difficulty":46,"q":213,"a":214},"typeof-array","How do you reliably check if a value is an array?","Use **`Array.isArray(value)`**. You can't use `typeof`, because arrays are\nobjects, so `typeof []` returns `'object'` — indistinguishable from a plain\nobject or `null`.\n\n```js\ntypeof []              \u002F\u002F 'object'  \u003C- useless for arrays\nArray.isArray([])      \u002F\u002F true\nArray.isArray({})      \u002F\u002F false\nArray.isArray('abc')   \u002F\u002F false\n```\n\n`Array.isArray` is also more robust than the older\n`value instanceof Array` trick, because `instanceof` **breaks across\nrealms** (e.g. an array coming from an `\u003Ciframe>` has a different `Array`\nconstructor, so `instanceof` returns `false`). `Array.isArray` works\nregardless of realm.\n",{"id":216,"difficulty":67,"q":217,"a":218},"nan","Why is NaN === NaN false, and how do you test for NaN?","`NaN` (\"Not-a-Number\") is, by the IEEE-754 spec and ECMAScript, the **only\nvalue not equal to itself** — there are many distinct computations that produce\n\"not a number,\" so the standard defines all comparisons with `NaN` (including\n`NaN === NaN`) as `false`. You therefore can't detect it with `===`.\n\n```js\nNaN === NaN          \u002F\u002F false\n0 \u002F 0                \u002F\u002F NaN\nNumber('abc')        \u002F\u002F NaN\n\nNumber.isNaN(NaN)    \u002F\u002F true  reliable\nNumber.isNaN('abc')  \u002F\u002F false (no coercion — 'abc' simply isn't NaN)\n\nisNaN('abc')         \u002F\u002F true  misleading: it coerces 'abc' -> NaN first\n```\n\nUse **`Number.isNaN(value)`** (ES2015), which checks \"is this *literally* the\nNaN value.\" Avoid the **global `isNaN`**, which coerces its argument to a number\nfirst and so reports `true` for plenty of non-NaN inputs. (Another trick that\nworks: `value !== value` is `true` only for `NaN`.)\n",{"id":220,"difficulty":46,"q":221,"a":222},"object-is","What is Object.is and how does it differ from ===?","`Object.is(a, b)` is \"same-value\" equality. It behaves like `===` **except** for\ntwo edge cases: it treats `NaN` as equal to itself, and it distinguishes `+0`\nfrom `-0`.\n\n```js\nObject.is(NaN, NaN)  \u002F\u002F true   (=== gives false)\nObject.is(0, -0)     \u002F\u002F false  (=== gives true)\nObject.is(1, 1)      \u002F\u002F true\n```\n\nIt's the same algorithm React uses to decide whether state\u002Fprops changed. Use\n`===` for normal comparisons; reach for `Object.is` when those two `NaN`\u002F`-0`\nedge cases actually matter.\n",{"id":224,"difficulty":67,"q":225,"a":226},"plus-minus-zero","What is the difference between +0 and -0?","JavaScript has a signed zero: `+0` and `-0` are distinct bit patterns. They're\n**equal** under `==` and `===`, but distinguishable with `Object.is` or by\ndividing (which exposes the sign via `Infinity`).\n\n```js\n+0 === -0           \u002F\u002F true\nObject.is(+0, -0)   \u002F\u002F false\n1 \u002F +0              \u002F\u002F Infinity\n1 \u002F -0              \u002F\u002F -Infinity\n```\n\n`-0` arises from operations like `-1 * 0` or `Math.round(-0.1)`. It rarely matters,\nbut can surprise you in sign-sensitive math or when used as a Map key.\n",{"id":228,"difficulty":34,"q":229,"a":230},"typeof-function","What does typeof return for a function?","`typeof` returns `'function'` for any callable — function declarations,\nexpressions, arrows, classes, and methods. This is a special case: functions are\ntechnically objects, but `typeof` singles them out.\n\n```js\ntypeof function () {}  \u002F\u002F 'function'\ntypeof (() => {})      \u002F\u002F 'function'\ntypeof class {}        \u002F\u002F 'function' (classes are functions under the hood)\ntypeof Math.max        \u002F\u002F 'function'\ntypeof []              \u002F\u002F 'object' (arrays are NOT singled out)\n```\n\nIt's the reliable way to check \"is this callable?\" before invoking\n(`typeof cb === 'function'`).\n",{"id":232,"difficulty":46,"q":233,"a":234},"typeof-results","What are all the possible results of typeof?","`typeof` returns one of eight strings:\n\n```js\ntypeof 'a'        \u002F\u002F 'string'\ntypeof 1          \u002F\u002F 'number'\ntypeof true       \u002F\u002F 'boolean'\ntypeof undefined  \u002F\u002F 'undefined'\ntypeof 10n        \u002F\u002F 'bigint'\ntypeof Symbol()   \u002F\u002F 'symbol'\ntypeof function(){}\u002F\u002F 'function'\ntypeof {}         \u002F\u002F 'object'  (also arrays, null, dates, etc.)\n```\n\nThe famous quirk is **`typeof null === 'object'`** — a bug kept for backward\ncompatibility. Anything not in the first seven categories (including arrays and\n`null`) reports `'object'`, which is why you need `Array.isArray` and\n`=== null`.\n",{"id":236,"difficulty":67,"q":237,"a":238},"boxing","What is autoboxing of primitives?","Primitives have no methods, yet `'hi'.toUpperCase()` works because JavaScript\ntemporarily **wraps** the primitive in its object form (`String`, `Number`,\n`Boolean`), calls the method, then discards the wrapper.\n\n```js\n'hi'.length        \u002F\u002F 2 — boxed to a String object momentarily\n(5).toFixed(2)     \u002F\u002F '5.00' — boxed to Number\n\n\u002F\u002F explicit wrapper objects are an anti-pattern:\nconst n = new Number(5)\ntypeof n           \u002F\u002F 'object', not 'number'\nn === 5            \u002F\u002F false\n```\n\nNever use `new Number`\u002F`new String` — the resulting objects break `===` and are\nalways truthy (`new Boolean(false)` is truthy!). Let autoboxing happen\nimplicitly.\n",{"id":240,"difficulty":46,"q":241,"a":242},"parseint-vs-number","What is the difference between parseInt and Number?","- **`Number(x)`** converts the **entire** string to a number; any invalid\n  character makes the whole thing `NaN`. Handles decimals.\n- **`parseInt(x, radix)`** reads an **integer prefix**, stopping at the first\n  non-numeric character, and ignores the rest. Always pass the radix.\n\n```js\nNumber('42px')      \u002F\u002F NaN\nparseInt('42px', 10)\u002F\u002F 42 (parses the leading 42)\nNumber('3.14')      \u002F\u002F 3.14\nparseInt('3.14', 10)\u002F\u002F 3 (integer only)\nparseInt('0x1F', 16)\u002F\u002F 31\nNumber('')          \u002F\u002F 0  (parseInt('') is NaN)\n```\n\nUse `Number`\u002F`parseFloat` for exact full-string conversion; `parseInt` for\nextracting a leading integer (e.g. `'20px'` -> `20`).\n",{"id":244,"difficulty":67,"q":245,"a":246},"eqeq-steps","What steps does == take to coerce operands?","The abstract equality (`==`) algorithm, simplified:\n\n1. Same type -> compare like `===`.\n2. `null == undefined` -> `true` (and they equal nothing else).\n3. number vs string -> convert the **string to a number**.\n4. boolean vs anything -> convert the **boolean to a number** (`true`->1, `false`->0).\n5. object vs primitive -> convert the **object to a primitive** (`valueOf`\u002F\n   `toString`), then retry.\n\n```js\n'5' == 5      \u002F\u002F string->number: 5 == 5 -> true\ntrue == 1     \u002F\u002F boolean->number: 1 == 1 -> true\n[] == 0       \u002F\u002F [] -> '' -> 0; 0 == 0 -> true\n[] == ![]     \u002F\u002F ![] is false->0; [] -> 0; true\n```\n\nKnowing these steps explains every \"wat\" example — and is the best argument for\nalways using `===`.\n",{"id":248,"difficulty":67,"q":249,"a":250},"toprimitive","How does an object convert to a primitive (Symbol.toPrimitive)?","When an object is used where a primitive is expected, JS calls its\n`Symbol.toPrimitive` method (if present) with a **hint** (`'number'`, `'string'`,\nor `'default'`); otherwise it falls back to `valueOf` then `toString`.\n\n```js\nconst money = {\n  amount: 100,\n  [Symbol.toPrimitive](hint) {\n    return hint === 'string' ? `$${this.amount}` : this.amount\n  },\n}\n`${money}`   \u002F\u002F '$100'  (hint 'string')\nmoney + 1    \u002F\u002F 101     (hint 'default' -> number)\nmoney * 2    \u002F\u002F 200     (hint 'number')\n```\n\nThis lets you control how objects behave in concatenation, arithmetic, and\ntemplate literals.\n",{"id":252,"difficulty":46,"q":253,"a":254},"valueof-tostring","What roles do valueOf and toString play in coercion?","Without `Symbol.toPrimitive`, object-to-primitive conversion uses these two: for a\n**number** hint it tries `valueOf` first; for a **string** hint it tries\n`toString` first. Whichever returns a primitive wins.\n\n```js\nconst obj = {\n  valueOf() { return 42 },\n  toString() { return 'hello' },\n}\nobj + 1       \u002F\u002F 43      (default\u002Fnumber hint -> valueOf)\n`${obj}`      \u002F\u002F 'hello' (string hint -> toString)\nString(obj)   \u002F\u002F 'hello'\nNumber(obj)   \u002F\u002F 42\n```\n\nOverride `toString` (and sometimes `valueOf`) to make your objects coerce\nsensibly in logs and expressions.\n",{"id":256,"difficulty":34,"q":257,"a":258},"template-coercion","How does coercion work in template literals?","Template literals coerce every interpolated value to a **string** (using\n`String()` \u002F the object's `toString`). This is convenient but can produce\n`[object Object]` or `undefined`\u002F`null` text.\n\n```js\n`count: ${5}`        \u002F\u002F 'count: 5'\n`arr: ${[1, 2, 3]}`  \u002F\u002F 'arr: 1,2,3' (array toString joins with commas)\n`obj: ${ {a: 1} }`   \u002F\u002F 'obj: [object Object]'\n`val: ${null}`       \u002F\u002F 'val: null'\n```\n\nFor objects, interpolate a specific field or `JSON.stringify(obj)` rather than\nrelying on the default `[object Object]`.\n",{"id":260,"difficulty":67,"q":261,"a":262},"json-stringify","What are the edge cases of JSON.stringify?","`JSON.stringify` silently **drops or transforms** several types: `undefined`,\nfunctions, and symbols are **omitted** in objects (or become `null` in arrays);\n`NaN`\u002F`Infinity` become `null`; `BigInt` **throws**; and `Date` becomes an ISO\nstring.\n\n```js\nJSON.stringify({ a: undefined, b: () => {}, c: NaN })\n\u002F\u002F '{\"c\":null}'  — a and b dropped, NaN -> null\nJSON.stringify([undefined, function(){}, 1])\n\u002F\u002F '[null,null,1]'\nJSON.stringify(10n) \u002F\u002F TypeError: BigInt not serializable\n```\n\nIt also calls a value's `toJSON()` if present, and ignores non-enumerable\nproperties. Know these when serializing for APIs or storage.\n",{"id":264,"difficulty":46,"q":265,"a":266},"float-precision","Why does 0.1 + 0.2 not equal 0.3 in JavaScript?","All JS numbers are 64-bit IEEE-754 **doubles**. Decimal fractions like `0.1` can't\nbe represented exactly in binary, so they're stored as the nearest approximation\nand rounding errors accumulate.\n\n```js\n0.1 + 0.2            \u002F\u002F 0.30000000000000004\n0.1 + 0.2 === 0.3    \u002F\u002F false\n\n\u002F\u002F compare with a tolerance instead:\nMath.abs(0.1 + 0.2 - 0.3) \u003C Number.EPSILON \u002F\u002F true\n```\n\nFor money, work in integer **cents** or use a decimal library. Never compare\nfloats with `===`; use an epsilon tolerance.\n",{"id":268,"difficulty":46,"q":269,"a":270},"bigint","What is BigInt and how does it differ from Number?","`BigInt` represents integers of **arbitrary precision**, beyond the safe integer\nlimit of `Number` (`2^53 - 1`). Create one with an `n` suffix or `BigInt()`.\n\n```js\nNumber.MAX_SAFE_INTEGER          \u002F\u002F 9007199254740991\n9007199254740991 + 2             \u002F\u002F 9007199254740992 (wrong! lost precision)\n9007199254740991n + 2n           \u002F\u002F 9007199254740993n (exact)\n```\n\nCaveats: you **can't mix** `BigInt` and `Number` in arithmetic (`1n + 1` throws),\n`typeof 1n === 'bigint'`, and it's integer-only (no decimals). Use it for large\nIDs, timestamps in nanoseconds, and exact big-integer math.\n",{"id":272,"difficulty":46,"q":273,"a":274},"nullish-coalescing","What is the nullish coalescing operator (??)?","`a ?? b` returns `b` **only when `a` is `null` or `undefined`** — unlike `||`,\nwhich falls back on **any** falsy value (`0`, `''`, `false`). This makes `??`\ncorrect for defaults where `0`\u002F`''` are valid.\n\n```js\nconst count = 0\ncount || 10   \u002F\u002F 10  treats valid 0 as \"missing\"\ncount ?? 10   \u002F\u002F 0   only null\u002Fundefined trigger the default\n\nconst name = '' ?? 'anon' \u002F\u002F '' (empty string is kept)\n```\n\nUse `??` when only \"absent\" should trigger the fallback. You can't mix `??`\ndirectly with `&&`\u002F`||` without parentheses (a syntax error by design).\n",{"id":276,"difficulty":34,"q":277,"a":278},"optional-chaining","What is optional chaining (?.)?","`?.` short-circuits to `undefined` if the value before it is `null`\u002F`undefined`,\ninstead of throwing — letting you safely access deep properties, call maybe-\nmissing methods, and index possibly-absent values.\n\n```js\nuser?.address?.city        \u002F\u002F undefined if user or address is null\nuser?.getName?.()          \u002F\u002F calls only if getName exists\narr?.[0]                   \u002F\u002F safe index access\n```\n\nIt pairs naturally with `??`: `user?.name ?? 'anon'`. Note it stops at the first\nnullish link and only guards against `null`\u002F`undefined` — not other errors.\n",{"id":280,"difficulty":46,"q":281,"a":282},"array-equality","How do you compare two arrays or objects for equality?","`===` compares **references**, so two distinct arrays\u002Fobjects with identical\ncontents are **not** equal. You must compare contents yourself.\n\n```js\n[1, 2] === [1, 2]   \u002F\u002F false (different references)\n\n\u002F\u002F shallow array compare:\nconst eq = (a, b) => a.length === b.length && a.every((v, i) => v === b[i])\n\u002F\u002F quick (but flawed) deep compare:\nJSON.stringify(a) === JSON.stringify(b) \u002F\u002F order\u002Fundefined-sensitive\n```\n\n`JSON.stringify` works for simple data but breaks on key order, `undefined`,\nfunctions, and cycles. For robust deep equality, use a library (lodash\n`isEqual`).\n",{"id":284,"difficulty":34,"q":285,"a":286},"string-number-conversion","What are the ways to convert a string to a number?","Several, with different strictness:\n\n```js\nNumber('42')     \u002F\u002F 42      (whole string; '' -> 0, '4a' -> NaN)\nparseInt('42px', 10) \u002F\u002F 42  (leading integer)\nparseFloat('3.14m') \u002F\u002F 3.14 (leading float)\n+'42'            \u002F\u002F 42      (unary plus — concise coercion)\n'42' * 1         \u002F\u002F 42      (arithmetic coercion)\n```\n\n`Number()`\u002Funary `+` are strict (invalid -> `NaN`); `parseInt`\u002F`parseFloat` are\nlenient (parse a prefix). Always validate with `Number.isNaN` afterward when the\ninput is untrusted.\n",{"id":288,"difficulty":34,"q":289,"a":290},"boolean-double-bang","What does the double-bang (!!) operator do?","`!!x` coerces any value to its **boolean** equivalent. The first `!` converts to\nthe inverted boolean, the second `!` flips it back — giving the truthiness as a\nreal `true`\u002F`false`.\n\n```js\n!!'hello'   \u002F\u002F true\n!!0         \u002F\u002F false\n!!null      \u002F\u002F false\n!![]        \u002F\u002F true  (empty array is truthy)\nBoolean('hello') \u002F\u002F true — equivalent, more explicit\n```\n\nIt's a common idiom to normalize a value to a strict boolean (e.g. before\nreturning from a predicate or storing a flag). `Boolean(x)` does the same thing\nmore readably.\n",{"id":292,"difficulty":46,"q":293,"a":294},"symbol","What is a Symbol and what is it used for?","A `Symbol` is a **unique, immutable** primitive, mainly used as **non-colliding\nobject keys**. Every `Symbol()` is distinct, even with the same description.\n\n```js\nconst id = Symbol('id')\nconst obj = { [id]: 123 }\nobj[id]              \u002F\u002F 123\nSymbol('x') === Symbol('x') \u002F\u002F false — always unique\n```\n\nUses: private-ish keys (not enumerable in `for...in`\u002F`JSON.stringify`), and\n**well-known symbols** that customize behavior (`Symbol.iterator`,\n`Symbol.toPrimitive`, `Symbol.asyncIterator`). `Symbol.for('k')` accesses a\nshared global registry.\n",{"id":296,"difficulty":67,"q":297,"a":298},"array-coercion","How do arrays coerce to primitives?","An array's `toString` joins its elements with commas. So an empty array becomes\n`''`, a single-element array becomes that element's string, and `+` with a string\nconcatenates — leading to several classic surprises.\n\n```js\n[] + []        \u002F\u002F ''        (both -> '')\n[] + {}        \u002F\u002F '[object Object]'\n[1, 2] + [3]   \u002F\u002F '1,23'    ('1,2' + '3')\nNumber([])     \u002F\u002F 0         ('' -> 0)\nNumber([5])    \u002F\u002F 5         ('5' -> 5)\nNumber([1, 2]) \u002F\u002F NaN       ('1,2' -> NaN)\n```\n\nThese underpin the `[] == ![]` brain-teasers. The rule: array -> string (comma\njoin) -> then number if needed.\n",{"id":300,"difficulty":46,"q":301,"a":302},"nan-operations","Which operations produce NaN?","`NaN` results from **invalid or undefined mathematical operations** — converting\nnon-numeric strings, `0\u002F0`, `Infinity - Infinity`, or arithmetic on `undefined`.\nAnd `NaN` is **contagious**: any arithmetic with it yields `NaN`.\n\n```js\nNumber('abc')          \u002F\u002F NaN\n0 \u002F 0                  \u002F\u002F NaN\nMath.sqrt(-1)          \u002F\u002F NaN\nundefined + 1          \u002F\u002F NaN\nparseInt('xyz', 10)    \u002F\u002F NaN\nNaN + 5                \u002F\u002F NaN (propagates)\n```\n\nBecause it spreads, a single bad value can turn a whole calculation into `NaN` —\nguard inputs and check with `Number.isNaN`.\n",{"id":304,"difficulty":46,"q":305,"a":306},"undefined-not-defined","What is the difference between undefined and \"not defined\"?","**`undefined`** is a value: a declared variable that hasn't been assigned, or a\nmissing property. **\"Not defined\"** means the identifier doesn't exist at all —\naccessing it throws a **`ReferenceError`**.\n\n```js\nlet a\nconsole.log(a)         \u002F\u002F undefined (declared, no value)\nconsole.log(b)         \u002F\u002F ReferenceError: b is not defined\n\nconst obj = {}\nconsole.log(obj.x)     \u002F\u002F undefined (missing property, no error)\n```\n\nSo `undefined` is recoverable (it's just a value); a \"not defined\" reference is an\nerror. `typeof` is the safe way to check the latter without throwing.\n",{"id":308,"difficulty":46,"q":309,"a":310},"typeof-undeclared","Why doesn't typeof throw on an undeclared variable?","`typeof` is special-cased to **not throw** a `ReferenceError` for an undeclared\nidentifier — it returns `'undefined'`. This makes it the safe way to feature-\ndetect a global that may not exist.\n\n```js\ntypeof someUndeclaredVar  \u002F\u002F 'undefined' (no error)\nsomeUndeclaredVar         \u002F\u002F ReferenceError\n\n\u002F\u002F safe environment checks:\nif (typeof window !== 'undefined') { \u002F* browser *\u002F }\n```\n\nCaveat: this leniency does **not** apply to `let`\u002F`const` in the temporal dead\nzone — `typeof x` before a `let x` declaration still throws.\n",{"id":312,"difficulty":46,"q":313,"a":314},"immutable-primitives","What does it mean that primitives are immutable?","A primitive value itself can never be changed — operations that look like\nmutation actually create a **new** value. You can reassign the variable, but the\noriginal primitive is untouched.\n\n```js\nlet s = 'hello'\ns.toUpperCase()  \u002F\u002F 'HELLO' (a new string)\nconsole.log(s)   \u002F\u002F 'hello' (original unchanged)\ns[0] = 'J'       \u002F\u002F silently fails (or throws in strict mode)\ns = 'world'      \u002F\u002F reassigning the variable is fine\n```\n\nContrast objects\u002Farrays, which are mutable (you can change their contents in\nplace). Immutability is why primitives compare by value and are safe to share.\n",{"id":316,"difficulty":46,"q":317,"a":318},"string-comparison","How does JavaScript compare strings with \u003C and >?","Relational operators compare strings **lexicographically** by UTF-16 code unit,\ncharacter by character. This is case-sensitive (uppercase letters have lower code\npoints than lowercase) and not locale-aware.\n\n```js\n'apple' \u003C 'banana'  \u002F\u002F true\n'Z' \u003C 'a'           \u002F\u002F true  (90 \u003C 97)\n'10' \u003C '9'          \u002F\u002F true  (string compare: '1' \u003C '9')\n'10' \u003C 9            \u002F\u002F false (mixed -> numeric: 10 \u003C 9)\n```\n\nFor human-friendly, locale-aware ordering (accents, case-insensitive), use\n`a.localeCompare(b)`. Note mixed string\u002Fnumber comparisons coerce to numbers.\n",{"id":320,"difficulty":67,"q":321,"a":322},"deep-vs-shallow-equality","What is the difference between shallow and deep equality?","**Shallow** equality compares the top level: same reference, or matching primitive\nvalues \u002F first-level properties. **Deep** equality recursively compares nested\nstructures for equivalent contents.\n\n```js\nconst a = { x: { y: 1 } }\nconst b = { x: { y: 1 } }\na.x === b.x          \u002F\u002F false (different nested references)\n\u002F\u002F shallow: equal keys but nested refs differ -> not equal\n\u002F\u002F deep: recursively equal -> equal\n```\n\nReact uses shallow comparison for re-render decisions (props\u002Fstate), which is why\nmutating nested objects can be missed. Deep equality (lodash `isEqual`) is\ncostlier but compares full structure.\n",{"description":31},"JavaScript interview questions on primitive types, type coercion, == vs ===, truthy\u002Ffalsy values and checking types, with examples.","javascript\u002Ffundamentals\u002Fdata-types-coercion","Data Types & Coercion","vp9_844foLQtkq0K684OgyEhpivBJuIJMeUrLOlNC0k",1781808674571]