function hoisting like var

Ingvar von Schoultz ingvar-v-s at
Wed Jul 30 18:26:59 PDT 2008

Very brief!

Brendan Eich wrote:
> On Jul 26, 2008, at 2:07 PM, Ingvar von Schoultz wrote:
>> You can't get away from supporting this:
>>       {
>>           function a(){}
>>           var b = a;
>>       }
>> ES4 is planning to support function declarations locally
>> bound in blocks, so the above is valid ES4 code.
>> What you see above is function b() hoisting like var.
>> (I said b, not a.)
> What you said does not make sense. It's true that var b is hoisted to  
> the top of the program or function body. But it is not initialized  
> until control flows through the assignment b = a that is part of the  
> var declaration. So there is no capture problem.

Of course not, that's exactly my point. I'm showing that hoisting
can be done without capture problems, and largely re-using existing
well-known functionality.

b() is hoisted to the global scope, which means it's bound there
and visible throughout. At the top it's |undefined|, to protect
against capture problems in case the programmer inserts a local

         let c;
         function a(){}
         var b = a;

The function should become callable from just before we enter the
local scope at the {. Calling becomes safe at this spot.

>> There is no far-too-complicated split-scope complexity. There
>> is no capturing of variables that haven't been declared yet.
>> It's simple, intuitive, well-defined and well-behaved.
> Thanks, I agree. But it is not what you proposed.

What is not what I proposed? In which proposal did I not propose it?

> Again, from  
> Waldemar's original reply, but with your proposed {{}} interpolated  
> and the elided code amended to say what the consequence is:
> // outer scope
> function c() ...;
> // inner scope
> {{
>    if (foo) {
>      const c = 37;
>    }
>    ... c in your proposal must be hoisted to the {{,
>        so it can't be function c -- yet it can't be
>        initialized to 37 if foo is "falsy" ...
> }}

Since it's bound to {{ }}, it's visible and shadowing c() throughout
{{ }}. It's |undefined| at the top. It may be assigned 37 under
the if().

It should behave exactly as if you had declared it a var, with only
one difference: Assigning a second time throws an error.

> You could reply that const is new (sort of -- two browsers already  
> implement it one way, another treats it as var) and therefore should  
> always scope to { or {{, whichever is closer. But the point stands if  
> you replace const with function or var and hoist to the {{.

What point? It's what the programmer coded. I can't find the problem.
Please explain.

> Repeating  
> the next counter-example, with {{}} changes again, to track your  
> proposal since the original exchange with Waldemar:
> // outer scope
> function c() ...;
> // inner scope
> {{
>    function f() {
>      return c;
>    }
>    a = f();
>    if (foo) {
>      const c = 37;
>    }
>    b = f();
>    ... just what do a and b hold here? Was f's captured
>        variable rebound by the if statement? ...
> }}
> And so on.

Since it's bound to {{ }}, it's visible and shadowing c() throughout
{{ }}. It's |undefined| at the top. It may be assigned 37 under
the if(). If you're allowed to read an unassigned const, |a| will
be |undefined| and |b| will be either |undefined| or 37.

You say rebinding. Why? I'd expect a simple assignment. Please explain.

>> The above is the /exact/ functionality of function hoisting
>> like var, apart from using two names. You can refuse the
>> clearer syntax, but you can't refuse the above code and
>> functionality.
> I think I see the confusion now. Do you believe that in the var b =  
> a; code you wrote, both the binding of the var named b *and* its  
> initialization with the value of the function object denoted a are  
> hoisted? Hoisted up to what point?

The assignment stays where it's written.

> Waldemar wrote a while back: "Keep in mind that function assignments  
> hoist to the beginning of the scope in which the function is defined,  
> so your proposal won't work."

It's a minor limitation, not a showstopper, not even close. It
doesn't mean that it "doesn't work". The arrangement is intentional,
acceptable, easy to understand and very useful.

The typical use case is if(). Then this behavior is exactly what
you expect.

> The word "assignment" where "definition" was perhaps more precise  
> (function definitions replace extant properties of the same name in  
> the variable object, they are not equivalent to assignment  
> expressions) may have misled you. From the context and the long- 
> standing spec and implementation behavior with functions not in  
> blocks or any other sub-statement position, it was clear (I think)  
> what was meant, but I can see how this could be confusing.
> Assignment expressions and initializers in var statements do not  
> hoist or otherwise move in the flow of control.

I'm well aware of all this.

Ingvar von Schoultz

------- (My quirky use of capitals in code comes from my opinion that
reserved and predefined words should all start with lowercase, and
user-defined should all start with uppercase, because this will easily
and elegantly prevent a host of name-collision problems when things
like programming languages are upgraded with new labels.)

More information about the Es4-discuss mailing list