Why are anonymous function expressions and named function expressions initialized so differently?

◇◆丶佛笑我妖孽 提交于 2019-12-04 08:26:54

The reason for all that "dancing" is simple.

The identifier of named function expression needs to be made available within function scope but not outside.

typeof f; // undefined

(function f() {
  typeof f; // function
})();

How do you make f available within function?

You can't create binding in the outer Lexical Environment since f shouldn't be available outside. And you can't create binding in the inner Variable Environment since... it's not yet created; the function is not yet executed at the moment of instantiation, and so 10.4.3 (Entering Function Code) step with its NewDeclarativeEnvironment has never happened.

So the way this is done is by creating an intermediate lexical environment that "inherits" directly from current one, and which is then passed as [[Scope]] into the newly created function.

You can see this clearly if we break steps in 13 into pseudo code:

// create new binding layer
funcEnv = NewDeclarativeEnvironment(current Lexical Environment)

envRec = funcEnv
// give it function's identifier
envRec.CreateImmutableBinding(Identifier)

// create function with this intermediate binding layer
closure = CreateNewFunction(funcEnv)

// assign newly created function to an identifier within this intermediate binding layer
envRec.InitializeImmutableBinding(Identifier, closure)

So the lexical environment within f (when resolving identifier, for example) now looks like this:

(function f(){

  [global environment] <- [f: function(){}] <- [Current Variable Environment]

})();

With anonymous function it would look like this:

(function() {

  [global environment] <- [Current Variable Environment]

})();
o.v.

The core difference between the two deals with scoping (although, if nothing else, it's curious to see how much is actually involved in doing so ;) - and you're already correct in pointing out the main difference between named/anonymous function expresssions being the ease of calling the named ones recursively.

Why do I say ease? Well, nothing actually prevents you from calling an anonymous function recursively but it's just plain not pretty:

//silly factorial, 5!
(function(n) {
  if (n<=1) return 1;
  return (n*arguments.callee(n-1)); //arguments.callee is so 1990s!
})(5);

In fact, that's exactly what MDN says in describing named function expressions!

If you want to refer to the current function inside the function body, you need to create a named function expression. This name is then local only to the function body (scope). This also avoids using the non-standard arguments.callee property.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!