0

Consider the following named function:

function f() {
    return f.apply(this, arguments);
}

If you call this function normally it would result in a stack overflow as expected. Not very interesting. So let's do some magic:

var g = f, f = alert;

Now if you call f it will simply alert the first argument. However if you call g it will still alert the first argument. What's happening? Shouldn't calling g result in a stack overflow?

What I understand is that inside the function f (now g) the variable f is no longer bound to f. It becomes a free variable. Hence inside f the variable f now points to alert.

Why does this happen? I would expect the function name inside a named function to always refer to the function itself. I'm not complaining. It's actually pretty cool. I'm just curious.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • When I run `var g = f, f = alert; g()` it throws a `TypeError` (undefined is not a function) as I expected (because `g` is assigned the value of `f` while `f` is undefined). How are you guys ending up with `g` as `alert`? Or is that second block supposed to be run in the same context as the first? – Dagg Nabbit Jan 06 '14 at 02:45
  • That's not what the OP is doing, he's doing: `var f = function () {...}, g = f, f = alert; g();` – Abdullah Jibaly Jan 06 '14 at 02:49
  • Ah, ok, I thought they were two separate examples. You're right then, no recursion because `f` is reassigned – Dagg Nabbit Jan 06 '14 at 02:50

2 Answers2

4

When you do:

var g = f

This is effectively the same as:

var g = function () {
    return f.apply(this, arguments);
}

However, since you reassigned f it no longer points to the original function, it now points to alert. Looks like it's working as designed.

Abdullah Jibaly
  • 53,220
  • 42
  • 124
  • 197
1

As mentioned by other answers, it's as designed. Basically, apart from hoisting, the declaration is doing this:

var f = function () {
    return f.apply(this, arguments);
}

That is to say, the value of f is not resolved at the declaration but rather during the function call. Which is why you're seeing what you're seeing.

But there is a way to force it to behave the way you want: use a named function expression. A named function expression looks like a declaration but is not due to the function being declared as an expression.

For example, in the following:

var ff = function f () {
    return f.apply(this, arguments);
}

The value of f is bound at the declaration and will be immune to reassignment. In a named function expression, the function name is only defined inside the expression and therefore behaves more like a closure rather than a variable.

Community
  • 1
  • 1
slebetman
  • 109,858
  • 19
  • 140
  • 171