Thursday, July 11, 2013

JavaScript Hoisting

J
ust in case there weren't enough clueless people on the web trying to figure this out, I thought I'd add a bit here in the interest of my figuring it out. In the process of processing JavaScript (Note to Self: see "JavaScript Interpreter"), things are sometimes executed in ways you might not be expecting. This can change the scope of some variables and functions and confuse the bejeezus out of anyone who isn't paying enough attention. Including me.

Some definitions:
  • A JS function expressionvar myCode = function(a, b) {stuff;};
  • A JS function declarationfunction myCode (a, b) {stuff;};
  • Hoisting: Pretty much what it sounds like, some things are lifted upwards and therefore are executed sooner in the script than where they were originally written. So, something that was written like so:
    function bunny (){
      rabbit();
      var carrot= 2;
    }

    Is actually executed like so:
    function bunny (){
      var carrot;  //declaration gets hoisted
      rabbit();
      carrot= 2;  //assignment stays put
    }
So, what does this have to do with scope? JS has function level scope. Blocks do not create a new scope (eg if statements do not create a new scope, only functions can. Compare/contrast with a C-based language). This is actually the most helpful block of text (from Ben Cherry) that I've read while pondering all this:
In JS, a name enters a scope in in one of four ways:
  1. Language-defined: All scopes are, by default, given the names this and arguments.
  2. Formal parameters: Functions can have named formal parameters, which are scoped to the body of that function.
  3. Function declarations: These are of the form function foo() {}.
  4. Variable declarations: These take the form var foo;.
Function declarations and variable declarations are always moved (“hoisted”) invisibly to the top of their containing scope by the JavaScript interpreter. Function parameters and language-defined names are, obviously, already there. [names are also resolved in this order] [emphasis mine]

When to use what: If you're aware of hoisting and how things will execute, then use whatever floats your boat, even intentionally use hoisting to get your function declaration moved up. Because a function declaration is all declaration, not declaration plus assignment as in a function expression, the entire function gets hoisted at once. A function expression only has the "var blah;" hoisted, leaving the actual function body (assignment expression) behind. (see also Angus Croll on function declarations/expressions. The fifth time I came back to that page I got the right quiz answers (and not just from memorizing them)).

How functions execute with hoisting when a script runs (examples taken from my hangout on air with JBeach):
Written like so:
(function () {
  a = grapefruit;
  b = applesauce;

  function grapefruit () {  //function declaration
  return 'citrus in your eye';
  };
  var applesauce = function () {  //function expression
  return 'goodness';
  };
}());

Executed like so:
(function () {
  function grapefruit () {  //function declaration hoisted
  return 'citrus in your eye';
  };
  var applesauce; //function expression declaration (?) hoisted
  a = grapefruit;
  b = applesauce;

  applesauce = function () {  //function expression
  return 'goodness';
  };
}()); // returns "citrus in your eye" for a and "undefined" for b.


I think I've got the concept, even if I haven't quite got the details of how it runs (slight lack of applied JS knowledge here, but working on that).

There was also mention of NFEs and IIFEs somewhere along the way here. NFEs are nicer than anonymous functions should you need to track one through a tangle of code. IIFEs are somewhat also known as Self-Executing Anonymous Functions, but with some nuance. An IIFE does invoke itself, can be anonymous or named, and can be self-executing.
  • Named Function Expression (NFE): var myFunc = function myFunc (){code here}
  • Immediately Invoked Function Expression (IIFE): (function myFunc (){code here}());

Disclaimer: For goodness' sake don't take my word for any/all of this if you've just stumbled across my blog. I'm just learning and these posts are essentially my notes. Please do read the articles I've linked to, read as much as you can, experiment, and figure it all out for yourself, too.

And if I've got something not quite right or downright wrong, please leave a comment. Thank you...

No comments:

Post a Comment