Why Use Named Function Expressions

Why use named function expressions?

In the case of the anonymous function expression, the function is anonymous — literally, it has no name. The variable you're assigning it to has a name, but the function does not. (Update: That was true through ES5. As of ES2015 [aka ES6], often a function created with an anonymous expression gets a true name [but not an automatic identifier], read on...)

Names are useful. Names can be seen in stack traces, call stacks, lists of breakpoints, etc. Names are a Good Thing™.

(You used to have to beware of named function expressions in older versions of IE [IE8 and below], because they mistakenly created two completely separate function objects at two completely different times [more in my blog article Double take]. If you need to support IE8 [!!], it's probably best to stick with anonymous function expressions or function declarations, but avoid named function expressions.)

One key thing about a named function expression is that it creates an in-scope identifier with that name for the function within the functon body:

var x = function example() {

console.log(typeof example); // "function"

};

x();

console.log(typeof example); // "undefined"

How do named function expressions work?

Yes, that code works in the same way in browsers, so let's explain a little your code.

Declaring the function

function NF() { return true }
// call NF() function will output
// true

Assigning function's value to variable

let _variable = NF();
// the code above, will execute FN function and then store it into variable _variable

Named function expression (function expression + declaration):

let _variable2 = function FN2() { return true; };
// The code above will create FN2 function and then assign it to the variable called _variable2.
// note that the function only will assign it to the _variable2 but no its results because is not executed yet
// this _variable2() will call FN2 function that was assigned to the _variable2

finally this code

let variable3 = function NF3() { return true; }();
// call variable3 will output true.
// Why calling variable3 output the returning value from NF3 function ?
// that is because you are declaring and in the same way executing the NF3 function and then assign it to the variable3

So why calling typeof NF2 is undefined ? , that is because NF2 function it's only available into the variable3's scope, the function called NF2 is not declared into the top level, I mean into the window scope for browser for example, so that FN2 function only lives into the variable that contains the function itself.

I hope it can help you.

Why is it called a function expression and not a function declaration?

Interesting question. Wording can have soft boundaries, which is partly what causes this confusion.

First, some rough definitions:

  • Expression: I think this is best thought of with an example. 2 * 2 is an expression, because you can continue doing other operations on it, like 2 * 2 - 3. if (...) on the other hand is not an expression in javascript, it does not resolve into a value in which you can do further operations on. It's invalid to do if (...) {...} / 2. This "definition" has some holes, but should be good enough for the purposes of this answer.
  • Declaration: A declaration is just declaring that you're going to use a certain variable name. So, in your example with const foo = 5, It's the const foo part that actually declares, the = 5 is just initializing the declaration.

With that in mind, lets consider two examples of functions, and see how this terminology plays into these examples:

  1. const g = function() {}

There is a declaration going on here, the const g. We also see that there's a function being created and assigned to g. We're doing something with the result of function() {} (we're assigning it to something), which means javascript will interpret this as a function expression.


  1. function f() {}

We're not using this function as an expression (we're not doing any additional operations with it after we create it), so javascript will actually treat this different from our first example. It's going to declare f in the local namespace, and it's going to apply function hoisting. If you simply added a + before function f() {}, then you would cause javascript to interpret this as an expression instead, and these features will be disabled. You can try it in the dev tools - put in +function f(){} or const g = function f(){}, and f will remain undefined.

The fact that javascript treats the function keyword differently in different contexts is at the heart of this language choice. You can almost think of the "function" keyword as having two different meaning, and the chosen meaning depends on the context. Any time the function is used in an expression context or when it's unnamed, then it takes on the "expression" form, and we call it a "function expression". Otherwise, it takes on the alternative form that contains some extra features, like declaring the function name as a local variable. This is probably why we've adopted to call this alternative form a function declaration. However, like the OP rightly pointed out, even with const f = function() {}, a declaration is still happening, it's just not happening by the function keyword.

Footnote: With examples like +function f() {}, f won't be defined in the same scope as g, but it does get defined inside the body of f. In other words, any named function expression declares its name within its own body.

Why do function declarations get hoisted and function expressions don't?

As per MDN,

Conceptually, for example, a strict definition of hoisting suggests that variable and function declarations are physically moved to the top of your code, but this is not in fact what happens. Instead, the variable and function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code.

As you see, in a function expression, actual function is a value assigned to a named variable. So this named variable is hoisted. Even if you have a named function assigned, it still will not be hoisted as it is not a declaration and will be created later.

Sample:

function test() {

console.log(fn, foo);



var fn = function foo() {}

}

test();

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

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]

})();

Advantages to use function expression instead of function declaration?

Along with that very good answer, the only advantage I can see is dynamically changing a function call.

For example this code :

function foo(){
console.log('foo');
}

function bar(){
console.log('bar');
}

var myFn = foo;

myFn();

setInterval(function(){
if(myFn === foo) myFn = bar;
else myFn = foo;
}, 5000);

setInterval(function(){
myFn()
}, 6000);

It will never log the same thing since you reassign a global variable, every innerscope function will change while this code :

function foo(){
console.log('foo');
}

setInterval(function(){
function foo(){
console.log('Changed foo');
}

foo()
}, 5000)

setInterval(function(){
foo()
}, 5000)

Will log 2 different things. You can only change the current scope function, not the global.



Related Topics



Leave a reply



Submit