admin管理员组

文章数量:1414869

In an online course, Kyle Simpson says the following code demonstrates the necessity of hoisting in javascript, because without hoisting "one of the functions would always be declared too late."

a(1)  // 39

function a(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

function b(foo){
  return c(foo) + 1
}

function c(foo){
  return a(foo*2)
}

But this works just fine.

var a = function(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

var b = function(foo){
  return c(foo) + 1
}

var c = function(foo){
  return a(foo*2)
}

a(1) // 39

So what's the story? Convenience and placement of invocation aside, are there any situations that require hoisting?

In an online course, Kyle Simpson says the following code demonstrates the necessity of hoisting in javascript, because without hoisting "one of the functions would always be declared too late."

a(1)  // 39

function a(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

function b(foo){
  return c(foo) + 1
}

function c(foo){
  return a(foo*2)
}

But this works just fine.

var a = function(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

var b = function(foo){
  return c(foo) + 1
}

var c = function(foo){
  return a(foo*2)
}

a(1) // 39

So what's the story? Convenience and placement of invocation aside, are there any situations that require hoisting?

Share Improve this question asked Apr 25, 2016 at 5:19 rswerverswerve 1791 gold badge3 silver badges8 bronze badges 7
  • second one will give error if a(1) is called before function definitions. – gurvinder372 Commented Apr 25, 2016 at 5:20
  • Yes, but that's trivially true of any function and its invocation (as I note in the question). Simpson's claim seems to be that the mutually recursive structure of the functions requires hoisting, but that doesn't seem to be the case. – rswerve Commented Apr 25, 2016 at 5:22
  • In the first one, if you give a(1) before function definition, it won't give the same error. – gurvinder372 Commented Apr 25, 2016 at 5:27
  • Thanks for taking the time to ment, but you're missing the point of the question. – rswerve Commented Apr 25, 2016 at 5:30
  • hoisting lets you put functions below instead of above the executing code. – dandavis Commented Apr 25, 2016 at 5:49
 |  Show 2 more ments

3 Answers 3

Reset to default 7

The claim I've made about a non-hoisted JS being unable to support mutual recursion is just conjecture for illustration purposes. It's designed to help understand the need for the language to know about variables available in the scope(s). It's not a prescription for exact language behavior.

A language feature like hoisting -- actually hoisting doesn't exist, it's just a metaphor for variables being declared in scope environments ahead of time during pilation, before execution -- is such a fundamental characteristic that it can't easily be reasoned about when separated from the rest of the language's characteristics.

Morever, it is impossible to fully test this hypothesis in just JS. The snippet in the OP only deals with part of the equation, which is that it uses function expressions instead of function declarations to avoid function hoisting.

The language I was using to pare to for illustration is C, which for example requires function signatures to be declared in .h header files so that the piler knows what a function looks like even if it hasn't "seen" it yet. Without it, the piler chokes. That's a sort of manual hoisting in a sense. C does it for type checking, but one can imagine this sort of requirement existing for other reasons than that.


Another way of thinking about this is whether JS is a piled language where everything has been discovered before it executes, or whether it is interpreted top-down in a single pass.

If JS were top-down interpreted, and it got to the definition of an a() function that referenced a b() inside it that it hadn't seen yet, that could be a problem. If that call expression was handled non-lazy, the engine couldn't figure out at that moment what the b() call would be about, because b() hadn't been processed yet. Some languages are lazy and some are non-lazy.

As is, JS is piled first before execution, so the engine has discovered all the functions (aka "hoisting") before running any of them. JS also treats expressions as lazy, so together that explains why mutual recursion works fine.

But if JS had no hoisting and/or was not lazy, one can imagine the JS engine would be unable to handle mutual recursion because the circular reference between a() and b() would in fact mean that one of the two was always declared "too late".

That's really all I meant in the book.

Convenience and placement of invocation aside, there are not any situations that require hoisting.

Just make sure to declare all the functions before using them.

Note: In some browser, function a(){} creates a function with name a while var a = function(){} does not (considered anonymous function). The function name is used when debugging. You could also do var b = function a(){}.

The second block of code works fine because you are invoking a(1) after all the functions are initialized. Try the following block:

var a = function(foo){
  if (foo > 20) return foo
    return b(foo+2)
}

var b = function(foo){
  return c(foo) + 1
}

a(1);

var c = function(foo){
  return a(foo*2)
}

This will give an error Uncaught TypeError: c is not a function because function assigned to c is not hoisted. This is the reason why you need hoisting.

Because if you declare functions as in your first block of code, all the functions will be hoisted and you can invoke a anywhere in the code. This is not true in the other cases.

本文标签: Is hoisting really necessary in javascript to enable mutual recursionStack Overflow