12

I got an unexpected result. Here's the code:

b = function c() {
  console.log(c);
  c = 3;
  console.log(c);
}
b();

I thought the second console.log should print "3" but instead I got the function itself. Why?


Meanwhile, from the code below I got the right "3".

function ff() {
  ff = 3;
  console.log(ff);
}
ff();
Salman A
  • 262,204
  • 82
  • 430
  • 521
Leaf
  • 147
  • 5
  • Since you are new to JavaScript, you should look up hoisting, execution stack and first class functions. I think in the first case, the "c" is a function assigned to "b". When you invoke "b", it executes/invokes "c". Each function have a code property which is invokable (when following by () ), But when only accessed by name (in this case it is "c"'), only the code is printed and not invoked. I hope it helps you in some capacity. – Waleed Iqbal Dec 22 '17 at 07:29
  • See also [Where is the immutable binding record of the identifier in a named function expression stored in JavaScript?](https://stackoverflow.com/q/30547514/1048572) – Bergi Dec 22 '17 at 12:17

3 Answers3

11

You cannot overwrite the function's named variable inside its own declaration. NFE (Named Function Expression) name cannot be overwritten (because it is constant)

This is clear when you write in strict JS mode. Try the example below:

'use strict';

var b = function c(){
  console.log(c);
  c = 3; // c still is a function
  console.log(c);
}

b();
mehulmpt
  • 15,861
  • 12
  • 48
  • 88
8

You are using a function expression:

FunctionExpression :
function Identifieropt ( FormalParameterListopt ) { FunctionBody }

So b = function c() { ... }; is perfectly valid, strict mode or otherwise. What happens to c is another question. According to the specs:

The production
FunctionExpression : function Identifier ( FormalParameterListopt ) { FunctionBody }
is evaluated as follows:

[...]
3. Call the CreateImmutableBinding concrete method of envRec passing the String value of Identifier as the argument.
4. Let closure be the result of creating a new Function object as specified in 13.2 [...]
5. Call the InitializeImmutableBinding concrete method of envRec passing the String value of Identifier and closure as the arguments.
[...]

NOTE
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.

So:

  • c is visible inside the function but not outside it
  • c cannot be overwritten from inside the function (an "immutable" binding)

Meanwhile, from the code below I got the right "3".
function ff() {

This is a function declaration; different (and more obvious) rules apply here.

Salman A
  • 262,204
  • 82
  • 430
  • 521
1

This pattern of function declaration shown in the first example is called NFE (named function expression), while the othher one is called function declaration. The big difference is, in case of NFE, the function's name, i.e. c in our case lives only incide the scope of the function. Therefore, if you will try to call it by its' name from outside wou will get the error, c is not defined, which means c doesn't exist globally.

b = function c(){
  console.log(c);
    c=3;
  console.log(c);
}
c(); //c is not defined

Now look closely at the line c=3 inside the function body of c. What this code block normally would have done, is create a global variable by the name c, outside the function body, which would have been accessible, outside the function as well. But, here, as the c already lives inside the scope of the function's own body, it will not let you declare there, cause it will mean to overwrite its' own name, which is not allowed in case of NFE, (but allowed in case of function declaration, i.e, the second example in question). That is precisely why the assignment code, c=3 is not doing anything here.

To realize it more closely, you can update c=3 with var c=3, in which case it will let you declare a local variable by the name of c inside your function body, which you can then use inside the function.

b = function c(){
  console.log(c);
  var c=3;
  console.log(c);
}
b();
Sandip Ghosh
  • 719
  • 7
  • 13