Hoisting


Hoisting

hoisting is the processing of declarations before any code is executed.

What's a declaration though?

  • declaration is a way of telling the interpreter (or compiler) that a name or identifier exists
  • we learned a few ways of declaring names in JavaScript (the 1st is easy, the 2nd, a bit tricky)… what were they? →
    • variable declarations (using constlet, and var)
    • function declarations (using function f(x) {})


Hoisting basically brings declarations to the top of the current scope.  What does that mean for us? →

  • some declarations do not have to occur before they are used!
  • we already saw this with functions declarations...

 

Hoisting and Functions

So… we basically know what happens here. What's the output of the following code examples? →

f();
function f() {
	console.log("TO THE TOP, PLZ!")
}

outputTO THE TOP, PLZ!

f();
var f = function() {
	console.log("TO THE TOP, PLZ!")
}

outputTypeError: undefined is not a function

f();

output: ReferenceError: f is not defined

 

Hoisting and Functions SOME MORE!

What's the output of this code? →

function outer() {
	inner();
	function inner() {
		console.log('hello');	
	}
}
outer();

hello is printed out. What happens if we add in a call to inner at the end, outside of the function?

// same as above (function outer() ... )
// but add the line below at the very end
inner();

ReferenceError - function declarations are hoisted to the top of their current scope (not to the top of the global scope)

 

Great! But What About varDeclarations?

(We'll see why var f = function ... behaves the we way it does)

 

var Examples

Treating each code example as a completely separate program, what is the output of the following lines of code?

console.log(x);
// variable not yet declared (easy)
ReferenceError: x is not defined
var x;
console.log(x);
// x is declared before ... works obvs!
undefined
// so... what do we get here?
console.log(x);
var x;
undefined

Note that the last example would be an error if using let (or const)

 

var Declarations are Hoisted

console.log(x);
console.log(y);
var x;
var y;

In the above example:

  • the variable declarations are taken from the regular top-to-bottom flow
  • … and they are treated as if they were moved to the beginning of their enclosing scope
  • consequently, this prints out undefined twice rather than ReferenceError
 

How About Initializing a Variable Along with var?

Let's start simple. What's the output of this code?

var x = 5;
console.log(x);
// no surprise here!
5

But how about this? →

console.log(x);
var x = 5;
// oof. what!?
undefined
 

var and Initialization

  • var declarations are hoisted
  • but the initialization is executed in the location of the program where the initialization statement is actually placed
  • soooo… that means:
console.log(x);
var x = 5;
  • is executed as:
var x;
console.log(x);
x = 5;
 

Another Note on var and Hoisting

This probably doesn't matter since, we all know that you should never declare a variable without var, but:

  • implicit variable declarations are not hoisted!
  • the following gives us a ReferenceError
// (all we did here was take out var!)

console.log(x); // oops ... ReferenceError
x = 5;
 

What About let and const?

As we saw previously, let and const have a Temporal Dead Zone →

  • a variable declared with let and const cannot be accessed…
    • between the time that the containing scope is entered
    • and the actual let or var declaration
  • however… an identifier is actually created for it at the beginning of the scope! you just can't use it yet!
  • (so it's sort of hoisted; there seems to be some debate on the terminology for this)
  • consequently, this code gives an error:
    console.log(x);
    let x = 5;
 

Hoisting Summary

This is all you need to know about hoisting:

  • let and const declared variables cannot be accessed until their declaration (this is actually sane)
  • var declarations and function declarations are brought to the beginning of their enclosing scope
    • all function declarations are hoisted
    • var declarations are hoisted, but the assignment part occurs where the original statement was located
    • var declarations that haven't been assigned a value yet are initialized with undefined (just like let)
  • implicit variable declarations (no constlet, or var) are not hoisted (but you always use letconst or var, so not relevant, right?)
 

Hoisting Example 1

What's the output of the following code? →

var num = 1000; 
f(); 

function f(){ 
	console.log(num)
	var num = 5;
};
undefined
  • the global num is not used
  • instead, num within the function is hoisted to the top of its enclosing scope, the function, f
  • but note the initialization is executed in the place where it occurs… consequently, undefined
 

Hoisting Example 2

What's the output of the following code? →

console.log(f);
var f = function(x) {
	console.log("hello " + x);
}
  • yup, still undefined - the declaration is hoisted
  • … but the initialization to a value is not
// it's executed as if it were
var f;
console.log(f);
f = function(x) {
	console.log("hello " + x);
}
 

Hoisting Example 3

What's the output of this code? →

var inner = 1000;
function outer () {
    inner = 5;
    function inner() {}
}
outer();
console.log(inner);
  • the output is 1000
  • the function declaration of inner is hoisted to the top of the enclosing scope
  • which renders the first line of the function, inner = 5, a reassignment of the local inner, not the global
 

Hoisting Example 4

What's the output of this code? →

console.log(f);

const f = function() {
    console.log('I am function!');
}
ReferenceError   

(temporal dead zone for const and let; used before declared)

 

Back to an Earlier Mystery

And that's why the following gives us undefined is not a function →

f();
var f = function(x) {
	console.log("hello " + x);
}
  • we know that the declaration of f is hoisted
  • but it has no value at the point that it is invoked/called (it's undefined)
  • consequently, the program is attempting to use undefined as a function
 

Whew! That Seemed Unnecessarily Complicated.

Why even? ಠ_ಠ

 

No Seriously…

Hoisting.  Why? →

  • generally a top-down approach is taken to programming
    • so it may make sense for the "main" part of the program to go on top, calling functions elsewhere
    • those functions are likely to be declared below main
    • so it's more natural… ¯\_(ツ)_/¯ (maybe)
  • as for var, I don't know if I can excuse that!
  • according to this SO article….
  • it may possibly just be due to the interpreter implementation: scan source for variable and function declarations first, then execute code next
 

Easy, Right?

A lot of the design decisions in JavaScript seemed to be made for ease of use

Though, in certain cases these features actually make things more complex (weak typing, hoisting, etc.)
 

Hoisting Can Lead to Tricky Situations

Like the one we saw before:

var num = 1000; 
f(); 

function f(){ 
	console.log(num)
	var num = 5;
};

How can we get around this ambiguity? →

Always declare your variables at the beginning of your function!

Aaaaand… possibly, avoid using var; use let and const instead!

 
posted @ 2023-01-25 10:21  M1stF0rest  阅读(137)  评论(0编辑  收藏  举报