In a classic non-module script, what does `console.log(typeof foo); var foo = 1; function foo() {} console.log(typeof foo);` print, and why?
answer
- there is only one binding, not two
- setup pass, then execution
- function initialisation beats var initialisation
- the assignment is what runs later
- a bare var with no initialiser assigns nothing
basics
~20 sIt prints "function" and then "number". When the scope is entered, the function declaration wins and puts the function in the shared binding; the var declaration does not overwrite it. Later the assignment foo = 1 runs and replaces the value.
solid answer
~40 s`var foo` and `function foo` declare the *same* binding in the same scope, so there is only ever one `foo`. During the setup step that runs before any statement, `var` creates the name and would initialise it to `undefined`, but a function declaration for the same name initialises it with the finished function object — and a `var` never clobbers a binding that already exists. So the first `typeof` sees `"function"`. Then execution begins, the statement `foo = 1` runs (the `var` keyword contributes nothing more at run time), and the second `typeof` sees `"number"`. The general rule: at scope entry the function declaration wins; at run time whatever assignment executes last wins.
code
javascript · 15 linesfunction demo() {
console.log(typeof foo); // "function"
var foo = 1;
function foo() {}
console.log(typeof foo); // "number"
}
function noInitializer() {
var bar; // declaration only, no assignment runs
function bar() {}
console.log(typeof bar); // "function"
}
demo();
noInitializer();go deeper
Recall that a function declaration is already the function when the scope starts, so a typeof before any assignment reports "function", and that a later assignment simply replaces the value.
Walk the two phases explicitly: the setup pass where var creates the name and the function declaration initialises it, then execution where only the assignment statement has an effect.
Use it to show you read legacy code correctly — explain why duplicate declarations silently pick the last one, why source order between the two lines is irrelevant, and which lint rules catch the accident.
Treat it as evidence for the standard you set: names that mean two things are a defect, and the codebase policy plus lint configuration should make a duplicate or mixed declaration impossible to merge.
## One name, one binding The first thing to see is that this snippet does not create two variables. `var foo` and `function foo` are both var-scoped declaration forms, and in the same scope they name the same binding. Declaring a name twice with `var` is legal and harmless; mixing `var` with a function declaration is equally legal — the question is only what value the single binding holds at each moment. ## Step one: scope instantiation Before the first statement of a script or function body executes, the engine sets up the scope's bindings. Walking through it for this code: 1. It sees `var foo` and creates a binding named `foo`. If the binding is new, it is initialised to `undefined`. 2. It sees `function foo() {}`, builds the function object, and initialises `foo` with it — this step deliberately overwrites whatever the `var` left there. The order in the source does not matter for step 2: function declarations are processed after the plain `var` names, and a `var` never re-initialises a binding that already exists. So when the first statement finally runs, `foo` holds the function. ```js console.log(typeof foo); // "function" var foo = 1; function foo() {} console.log(typeof foo); // "number" ``` ## Step two: execution Now the statements run top to bottom. `var foo = 1;` splits into two halves: the declaration part, which already happened during instantiation, and the *assignment* part, which is an ordinary statement executed here. It stores `1` in the binding, replacing the function. The function declaration line does nothing at run time — its entire effect happened at instantiation. That is why moving it above or below `var foo = 1` changes nothing about the output. Swap the two lines and you still get `"function"` then `"number"`. A related detail: a bare `var foo;` with no initialiser is not an assignment at all, so it cannot clobber anything at run time: ```js function f() { var foo; // no assignment happens here function foo() {} return typeof foo; // "function" } ``` Many people expect `"undefined"` from that, reasoning that `var foo;` "resets" the name. It does not; it only ensures the binding exists, and the binding already does. ## Duplicate function declarations The same instantiation step explains what happens with two functions of the same name in one var scope: ```js function pick() { return "first"; } function pick() { return "second"; } console.log(pick()); // "second" ``` Both are processed in source order during instantiation, so the later one overwrites the earlier — and it wins even for calls written *above* both declarations, because the overwrite happened before any statement ran. No error, no warning; the first function is simply unreachable. Linters flag this (`no-redeclare` and `no-func-assign` in ESLint) precisely because a duplicate name is nearly always a merge accident or a copy-paste mistake. Note that the tolerant behaviour comes from these being *var-scoped* declaration forms. Block-scoped forms are stricter: two `let` declarations of the same name in one scope, or a `let` and a `var` colliding, are a `SyntaxError` caught before anything runs. ## Why this shows up in interviews On its face it is a whiteboard trick, but it tests something real: whether you can separate *when a binding is created and initialised* from *when an assignment executes*. Candidates who think "hoisting moves code to the top" get this wrong, because under that mental model the function declaration would have moved above `var foo = 1` and then been overwritten — which is right here by accident, but produces the wrong answer for the first `typeof`. Candidates who think in terms of an instantiation pass followed by ordinary execution get both lines right and can also explain the `var foo;` variant and the duplicate-declaration case. In real code, of course, none of this should appear. A single name meaning both a function and a number is a defect; the value of knowing the rules is being able to read the intent out of code someone else already wrote, and to explain a confusing stack trace in a legacy file.
- Does the answer change if the function declaration is written before the var statement?No — the output is still `"function"` then `"number"`. Both declarations are processed during the scope-setup pass regardless of their order, and the function's initialisation wins there. At run time the only statement with an effect is `foo = 1`, so the second reading is a number either way.
- What if the code is `var foo;` with no initialiser, alongside `function foo() {}`?`typeof foo` stays `"function"` throughout. A `var` with no initialiser contributes nothing at run time — it only guarantees the binding exists, and it already does. Nothing ever overwrites the function object, which surprises people who read `var foo;` as resetting the name to `undefined`.
- What happens with two function declarations of the same name in one scope?The later one wins, silently, and it wins even for calls written above both — the overwrite happens during scope setup, before any statement runs. There is no error because function declarations are var-scoped, unlike `let`, where a duplicate is a `SyntaxError`. Linters flag it since it is almost always a merge mistake.
saying these in an interview costs you the question
- Says the two declarations create two separate variables
- Answers "undefined" then "number", ignoring function initialisation
- Claims mixing var and a function of the same name is a SyntaxError
- Thinks the source order of the two declarations changes the result
- Believes a bare var with no initialiser resets the binding to undefined