Which design pattern(s) take advantage of JavaScript's hoisting behavior?
Asked Answered
I

9

50

Ben Cherry's excellent article explains hoisting in JavaScript adequately. My problem, however, is that I cannot conceive a use case for this notorious perpetrator of confusion. Please explain if there is a design pattern that actually takes advantage of this language feature.

Secondly, is scope hoisting unique to JavaScript?

UPDATE --- I'm adding a bounty for an answer that satisfies my curiosity: Which design pattern(s) actually take advantage of JavaScript's hoisting behavior? I understand why JavaScript supports hoisting, but I want to know how I can take advantage of this feature.

Intervenient answered 21/12, 2011 at 22:56 Comment(2)
All scoping techniques (even block scoping) are perpetrators of bugs and confusion. ...For those that don't know and understand them. It's just another way of doing things.Motorbus
1. Put the variable declarations and nested function declarations at the top of the function. 2. What bugs and confusion?Agnew
G
22

Variable hoisting

One of the simplest uses of hoisting is variable hoisting. If we didn't have variable hoisting, this would throw a ReferenceError:

var bar = foo; 
var foo;

That doesn't seem immediately useful, but it allows us to do things like this:

var myCoolJS = myCoolJS || {};

This basically means what it looks like: myCoolJS is myCoolJS if it exists, or a new object if it doesn't. The second myCoolJS doesn't throw a ReferenceError if myCoolJS didn't already exist, because this variable declaration is hoisted.

This saves us from doing an awkward typeof myCoolJS != 'undefined' check.

Function hoisting

Function hoisting can be especially useful when combining multiple scripts into one. For example, I've created a lightweight build-time implementation of CommonJS modules. This provides the same module, require, and exports features that are found in node.js. I built the tool to allow required modules to be composed of multiple files. For example, require('/foo') could result in a module composed of two files, foo.js (the "body file") and foo.h.js (the "header file").

This allows the "body file" to have no knowledge of the free variables provided by the CommonJS modules environment; all of that is handled in the header. This makes code reusable and easy to test without building. However, since the headers are prepended to the body, we leverage function hoisting in the body file to allow exports in the headers. For example:

// dom.h.js

var util = require('util').util;

exports.css = css; // we can do this because "css" is hoisted from below

// ... other exports ...

...

// dom.js

function css(){}; // this would normally just be an object.

css.hasClass = function(element) { ... };
css.addClass = function(element) { ... };

// ...other code...
Gastrotomy answered 5/3, 2012 at 16:51 Comment(2)
This is syntactic sugar at best, not a design pattern.Condolent
@Condolent the first case does boil down to syntactic sugar, but it's still a pattern you see a lot. Maybe not a design pattern though. The second case is basically a variation on the module pattern that takes advantage of function hoisting; I think it could qualify as a design pattern.Gastrotomy
F
16

Here's a use for hoisting:

(function() {
    var factorial = function(n) {
        if(n == 0)
            return 1;
        return n * factorial(n - 1);
    };
})();

Without hoisting, that wouldn't compile because factorial wouldn't exist yet inside the function literal. You'd have to declare the variable separately or use a named function.

JavaScript also allows code like the following:

var test = function(b) {
    if(b) {
        var value = 2;
    } else {
        var value = 5;
    }
    console.log(value);
};

With block scoping, you'd have to add another line to declare value before the if.

To be fair, this code works because of function scope, not hoisting. And JavaScript could have had function scope without hoisting. Ruby handles this better: Ruby has method scope for variables, but the variables don't exist until you set them:

def test(b)
    # unlike JavaScript, value is not accessible here
    if b
        value = 2
    else
        value = 5
    end
    puts value
end
Funiculate answered 3/3, 2012 at 4:32 Comment(4)
Your example of factorial seems to work in all scenarios I have tried, what am I missing / misunderstanding @Jordan Miner?Buckram
The example works fine in JavaScript because it has hoisting. The equivalent in other languages does not work: gist.github.com/jminer/19eaf18aaaa6e5fa7fd3 In at least Dart and D, you can't refer to the variable because it hasn't been defined yet. In JavaScript, you can refer to the variable before it is defined because of hoisting.Funiculate
Again, a self contained recursive function is more syntactic sugar than a design pattern.Condolent
How is declaring a variable twice in two separate branching conditions a good thing? It is not a good thing, it is a very very bad thing because it leads to even worse very bad things. Please do not do it.Adiell
F
10

JavaScript does not have block scope (let's forget about let for now) and thus any variable declaration is declaring for the entire function, of which JavaScript does have scope.

If you think about it that way, JavaScript hoisting may make more sense.

If you remember about hoisting, it shouldn't be a source of bugs and confusion. It's simply one of those quirks you must understand and remember.

I'm not sure if hoisting is limited to JavaScript. I've never heard of it elsewhere, but that doesn't necessarily mean it doesn't exist in other languages.

Fattish answered 21/12, 2011 at 23:2 Comment(4)
These are thoughtful comments, but I don't think you actually provide a use case for hoisting. Is it simply a style issue in your mind?Intervenient
VBScript variables are also hoisted (source).Spireme
@DavidRivers Think of the scope limited to functions. Therefore if you declare a function at the bottom of a function, you're really declaring it for the entire function because of its visibility is the entire function.Fattish
I think of it mainly as a matter of readability – utility functions may be declared at the bottom of a closure – but these examples are very good.Oceangoing
A
8

The first two examples in that article are just badly written. Bad code obviously leads to bugs and confusion. Let me give you the refactored versions of these examples. You will see that there is no confusion here...

Example 1 - Original code

var foo = 1;
function bar() {
    if (!foo) {
        var foo = 10;
    }
    alert(foo);
}
bar();

Example 1 - Refactored code (removed confusion)

var foo = 1;

function bar() {
    var foo;

    if ( !foo ) {
        foo = 10;
    }

    alert( foo );
}

bar();

The alert displays "10", and it's clear why. No confusion here.

Example 2 - Original code

var a = 1;
function b() {
    a = 10;
    return;
    function a() {}
}
b();
alert(a);

Example 2 - Refactored code (removed confusion)

var a = 1;

function b() {
    var a = function () {}; 
    a = 10;
    return; 
}

b();

alert( a );

The alert displays "1". Obviously. No confusion here, too.

Arianaariane answered 21/12, 2011 at 23:32 Comment(6)
This example is about function scope, not about hoisting.Leilanileininger
@Leilanileininger You have to go see the original code from the article first. The first example is about the local variable declaration being hoisted to the top, and the second example is about the function declaration being hosted to the top. I have refactored the code, so it only seems as if this is/was not about hoistng...Agnew
Excuse my confusion, but it looks as if all you've done is un-done the var hoisting, rather than show where it's useful. So is your answer simply "No, hoisting is useless"?Singularity
@Singularity The variable names of variable declaration statements are bound to the lexical environment (of the current execution context) before the code (any code) of the function body is evaluated. This process should be reflected by the source code. Therefore, variable declaration statements should be the first statements of a function body, and no other statements should be placed before them. Placing variable declaration statements "in the middle" of the code does not correspond to the underlying process and this disconnect can lead to errors (as OP's code clearly shows).Agnew
You are showing effectively how the code is interpreted. I agree one should write it that way in the first place, instead of allowing the engine to do what is right for us...Condolent
@ŠimeVidas You should have taken this to the logical conclusion that basing a design pattern on the implicit behavior of hoisting is a bad idea.Condolent
R
7

"hoisting" is not part of the ECMAScript Standard, but it does say that variable inside a function are declared at the begin of the function regardless of where in the function it is place in the code.

Example

(function() {
  alert(myvar); // undefined
  var myvar = 'local value';
})();

Internally Javascript would declare myvar before the alert, show the alert, then it would assign myvar to 'local value'.

So Javascript would interpet that code as:

(function() {
  var myvar;
  alert(myvar); // undefined
  myvar = 'local value';
})();

That is why "Java the Good parts" has a guideline that say you should declare variable at the top of your function.

Source: http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/

"Please explain if there is a design pattern that actually takes advantage of this language feature." "hoisting" is not a feature but rather a consequence how the Javascript interpreter structure the code since the language uses function-scoping.

"Which design pattern(s) actually take advantage of JavaScript's hoisting behavior? " Answer: None.

Romanic answered 6/3, 2012 at 15:35 Comment(1)
In my opinion, the only answer which addresses the actual question.Condolent
R
5

I think one area where hoisting is useful is due to the fact that functions are treated as first class objects. For example:

function foo()
{
   function a()
   {
      //...
   }

   function b()
   {
      //...
   }
}

can also be written as:

function foo()
{
   var a = function ()
   {
      //...
   }

   var b = function ()
   {
      //...
   }
}

Without hoisting, the following would result in an error:

function foo()
{
   var a = function ()
   {
      b();
   }
   a(); //Error in function since b is not yet defined

   var b = function ()
   {
      //...
   }
}

I suppose they could have only hoisted function objects, but I believe that would be inconsistent with the philosophy that functions should be treated as first class citizens in the language.

Rooseveltroost answered 21/12, 2011 at 23:19 Comment(5)
So long as a() is not executed immediately :)Fattish
Well, I guess people might point out that b would be defined when the foo object was "constructed", thus could have a value when a() was called even without hoisting, but hopefully you get the idea..Rooseveltroost
Your last example would be an error with hoisting - declarations are hoisted, not assignments. b will be undefined when you attempt to invoke it.Fattish
So, the hoisting behavior allows you to write modular functions that are able to reference each other, since it sidesteps reference errors caused by source order?Intervenient
How did this receive any upvotes? @Fattish is correct, this code will not work even with hoisting. Hoisting moves the declaration of b to the top, but the assignment remains where it curretnly exists - at the bottom.Condolent
P
4

Here's a real use case (albeit reduced to pseudo-code) for you, from someone who would actually want to use the benefits of hoisting in the wild.

I recently wrote this script for handling simple form validation and submission. As far as possible, each function declaration invokes the following. This has 2 chief benefits for readability:

  1. Logical sequence: there is a sequential flow to the code, which means functions will always be invoked before they are declared. This is a benefit in that, when used with low complexity functions, it keeps things relatively flat and gives you an idea of the calling context of a function shortly before its source. You will only ever have to scroll down (never up) to follow code, and — as far as possible — scroll not at all or very little.
  2. Low reference overhead: I like to keep all my variable declarations at the top of each scope so readers are aware of all the moving parts the function requires before reading through its body, but nobody wants to read every invoked function's source code to get an idea of what the current scope does. Using this method, you'll never run into a function reference before its declaration. It sounds stupid at first, but it actually reduces cognitive overhead: you're never given a function's source with the implicit remember this — we'll be using it later — instead you only ever read function source once you know the context it was called in.
$( function emailHandler(){
  var $form      = …
  var $email     = …
  var $feedback  = …
  var value      = …
  var validation = …

  // All initialisation is right here. Executes immediately.
  // Therefore, all future code will only ever be invoked
  // by call stacks passing through here.
  void function bindEvents(){
    $email.on( 'input', filterInput );

    $form.on( 'submit', preSubmit );
  }();

  function filterInput( inputEvent ){
    if( inputEvent && inputEvent.which === '13' ){
      return presubmit( inputEvent );
    }

    return validate();
  }

  function validate(){
    var presentValue = $email.val();

    if( validation.done || presentValue === value ){
        return;
    }
    else if( presentValue === placeholder || presentValue === '' ){
        validation.message = 'You must enter an email address to sign up';
        validation.valid   = false;
    }
    else if( !validation.pattern.test( presentValue ) ){
        validation.message = 'Please enter a valid email address';
        validation.valid   = false;
    }
    else {
        validation.message = '';
        validation.valid   = true;
    }

    validation.ever = true;

    clearFeedback();
  }

  function preSubmit( inputEvent ){
    if( inputEvent instanceof $.Event ){
      inputEvent.preventDefault();
    }

    if( !validation.ever ){
      validate();
    }
    if( validation.pending || validation.done ){
      return;
    }
    else if( validation.valid ){
      return submit();
    }
    else {
      return feedback();
    }
  }

  function submit(){
    $form.addClass( 'pending' );

    // Create an XHR base on form attributes
    $.ajax( {
      cache : false,
      url   : $form.attr( 'action' ),
      data  : $form.serialize(),
      type  : $form.attr( 'method' ).toUpperCase()
    } )
      .done( success )
      .fail( failure )
      .always( feedback );
  }

  function success( response ){
    validation.message = response.message;
    validation.valid   = response.valid;
  }

  function failure(){
    validation.message = 'Our server couldn\'t sign you up. Please try again later.';
    validation.error   = true;
  }

  function feedback(){
    clearFeedback();

    if( validation.message ){
      $feedback
        .html( validation.message )
        .appendTo( $placeholder );
    }

    if( !validation.valid || validation.error ){
      $form.addClass( 'invalid' );

      $email.trigger( 'focus' );
    }
    else {
      $form.addClass( 'done' );

      validation.done = true;
    }

    validation.pending = false;
  }

  function clearFeedback(){
    $form.removeClass( 'invalid pending done' );
  }
} );
Percussion answered 28/1, 2014 at 13:35 Comment(1)
I've been using this exact pattern for some time Barney, for the reasons you state. It promotes top-down reading, gives you context before implementation, and reduces cognitive overhead. I wrote a short blog post on this a while back with additional examples: jason.pincin.com/posts/function-hoisting-and-stylePerlman
D
1

I like the style of question, based on curiosity about the language. Obviously no one should actually use hoisting as a feature, unless they're absolutely certain that their home address cannot be discovered by those who may use it later.

I can only imagine a few trivial cases. The basic property to exploit is that the variable can be declared (but undefined) and then assigned in only one line of code, but with the events interpreted at two different distinct point.

With the declaration at the end of a loop (not .forEach as that of course sets scope) you could use this to detect the first iteration.

var notfirst = true;  // this is actually never referenced.

(function () {  
  var number, stack = [1, 2, 3, 4, 5];

  while (number = stack.pop()) {
    if (notfirst) console.log(number);
    var notfirst = true;
  }
})();

The output from emptying the stack is 4, 3, 2, 1. 5 is rejected.

Again. Don't do this!

Dodecagon answered 7/3, 2012 at 1:1 Comment(0)
F
0

If you consider the way other languages are written (C++/Java) and how their Class patterns are used, hoisting could be taken advantage of to write a similar pattern to build prototypes .

Frolick answered 2/5, 2016 at 18:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.