@jmar777's answer is definitely the best explanation and breakdown of the spec. However, I find that for us hands-on learners, a bit of illustrative code is helpful! ;)
The Basic Idea
- 'Declaration' makes a variable available throughout a given scope.
- 'Assignment' gives a variable a specific value at that location in the code. If you attempt to assign a value to a variable that has never been declared in that scope or a parent scope, then the variable is implicitly declared on the global scope (equal to typing
window.varName = value
).
- 'Initialization' is something that happens 'behind the scenes' or 'under the hood', so to speak. At runtime, all declared variables are initialized with a beginning assignment of
undefined
(even if they immediately get assigned a different value in the first line of code).
Thus, initialization isn't a term that matters to us. We declare and assign, and the Javascript engine initializes.
So to directly answer the example in your question:
var example;
declares a variable, which gets assigned a value of undefined
during initialization.
var example = "hi"
declares a variable, which also gets assigned a value of undefined
initially, but when that line of code is actually reached during execution, it gets re-assigned to the string "hi".
Illustrative Code
function testVariableDeclaration() {
// This behaves 'as expected'....
console.log(test2, 'no value assigned yet'); // --> undefined 'no value assigned yet'
// ....but shouldn't our actual expectation instead be that it'll throw an error since
// it doesn't exist yet? See the final console.log() below!
// As we all know....
test1 = 'global var'; // ....a variable assignment WITHOUT declaration in the current
// scope creates a global. (It's IMPLICITLY declared.)
// But a little counter-intuitively....
test2 = 'not global'; // Although this variable also appears to be assigned without
// declaration like 'test1', the declaration for 'test2' that
// appears *later* in the code gets hoisted so that it's already
// been declared in-scope prior to this assignment.
console.log( test1, window.test1 === test1 ); // --> 'global var' TRUE
console.log( test2, window.test2 === test2 ); // --> 'not global' FALSE
var test2; // As shown by the above console.log() outputs, this variable is scoped.
console.log( test3 ); // Throws a ReferenceError since 'test3' is not declared
// anywhere, as opposed to the first console.log() for 'test2'.
}
Effects of 'Scope' on Declaring/Assigning
What makes the difference between declaration and assignment even clearer is that declaring a variable always creates a new variable within the current scope (myVar
scopeB), even if a variable of the same name already existed in a parent scope (myVar
scopeA). Then we can assign a new value to myVar
scopeB at any point in the current scope without re-declaring it. (This assignment does not affect the value of myVar
scopeA.) Once the end of scopeB is reached, myVar
scopeB will no longer be available for assignment.
On the flip side, if we assign a value to a variable within a given scope without declaring it in that scope, it'll be assigned to the next-highest-up declaration in the 'scope chain' (or a global will be implicitly declared if no higher declaration is found).
Thus, a variable need only be declared once per scope, with each declaration overriding any declarations made in parent scopes (i.e. it creates a distinct variable). But it must be declared in-scope if it's meant to be unique to that scope. Assignment can happen as many times as desired, but only affects the most 'closely-declared' variable (for lack of a better term).