2
var foo = function bar(i) {
    bar = "change bar reference";
    if (i < 5) {
        setTimeout(function () {
            console.log(i);
            bar(++i);
        },
            1000
        );
    }
}

No errors for the above function.

var foo = function bar(i) {
    var bar = "change bar reference";
    if (i < 5) {
        setTimeout(function () {
            console.log(i);
            bar(++i);
        },
            1000
        );
    }
}

Error on the second function after adding in var to bar.

I expected both of the functions to throw an exception not just the second function with var bar.

I don't understand why only the second function throws an exception.

I get that the variable declaration with var will not overwrite the identifier "bar" but the assignment will do it at run time. I understand why var bar is a string not a function on the second function therefore throws an exception.

Why doesn't the first function throw exception? bar is clearly assigned to a string.

I read the documentation and got something below that might be useful.

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.

Does "the Identifier in a FunctionExpression cannot be referenced from" mean I can not do bar = "change bar reference"; in the 1st function?

What does the JavaScript script engine do when it sees bar = "change bar reference"? Does it just skip the line?

Edit: Uncaught TypeError: bar is not a function

foo(1)

8
  • Exactly what "exception" is it that you get? Commented Jun 21, 2018 at 13:10
  • you are changing function bar to a string, thats why you see the error. Commented Jun 21, 2018 at 13:11
  • 2
    The first one, your setting bar on the global scope,.. But you also have bar in local scope, and that will been seen first.. In the second one your creating both in local scope. If you changed the first one to say window.bar it will become more obvious what you have done. Commented Jun 21, 2018 at 13:12
  • @Pointy Uncaught TypeError: bar is not a function. Commented Jun 21, 2018 at 13:21
  • @Keith Im pretty sure that the first function does not have bar on global scope. bar is the identifier from the named function expression which can only be accessed inside the function. That is what make the problem interesting. Commented Jun 21, 2018 at 13:55

3 Answers 3

2

The reason for this is that as per the ES spec, there is an immutable binding on the named function's name:

From Section 14.1.20 of the ES6 spec:

  1. Let envRec be funcEnv’s EnvironmentRecord.
  2. Let name be StringValue of BindingIdentifier.
  3. Perform envRec.CreateImmutableBinding(name).

An immutable binding means that the identifier's value cannot be overwritten. Additionally, attempting to assign to it in strict mode should produce a runtime error:

12.14.1: In ECMAScript 2015, strict mode code containing an assignment to an immutable binding such as the function name of a FunctionExpression does not produce an early error. Instead it produces a runtime error.

Observe:

var foo = function bar() {
    bar = 2;
    console.log(bar);
};

foo();

So in your first example, your assignment to bar essentially has no effect unless it is in strict mode.

In the second one, you are shadowing the bar identifier by declaring a variable in a new scope. That's why bar retains the assigned value in the second example.

var foo = function bar() {
  var bar = 2;
  console.log(bar);
};

foo();

Sign up to request clarification or add additional context in comments.

12 Comments

I suspect that the assignment to bar does have an effect, that being the implicit creation of a global bar. It would probably throw an error if the function were in "strict" mode.
@Pointy I thought that it would create a global variable as well but it doesnt. Ran it as strict mode and it gave me "TypeError: Assignment to constant variable". So i guess bar identifier is a constant value that cant be changed and it doesnt throw error in non strict mode.
Ah OK, so it ends up behaving like a const symbol. I did not expect that but it's interesting.
it is a ploblem of the used browser, in edge, nothing happnes, no error/exception.
@JLRishe Ok, I finally understand everything about the problem. I can go to sleep now. Thank you.
|
1

At least when calling foo or bar, you get an error because the function does not exists anymore.

Why doesn't the first function throw exception? bar is clearly assigned to a string.

At runtime, without calling the function, nothing happens, but after the call of the function, the variable bar has a string as value, not a function for further calling for the timeout.

It depends. On Edge, it just skips the assignment, on Chrome it throws an error

Assignment to constant variable.

which means named function are implemented as const.

var foo = function bar(i) { // <<<---------+
        bar = "change bar reference"; // --+ tries to change global bar
        if (i < 5) {
            setTimeout(
                function () {
                    console.log(i);
                    bar(++i);
                },
                1000
            );
        }
    };

foo(0);

I read the documentation and got something below that might be useful. "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."

Does "the Identifier in a FunctionExpression cannot be referenced from" mean I can not do bar = "change bar reference"; in the 1st function?

Your question is unclear.

What does the JavaScript script engine do when it sees bar = "change bar reference"? Does it just skip the line?

No. It assign the value to either the local bar or the global bar, but if having a local or global variable the the local variable is used over the global one.

'use strict';
function foo() {
    var a = 'bar';
    console.log(a); // bar
}

var a = 42;

foo();
console.log(a); // 42

4 Comments

Sorry, i had some typos. Try to copy and paste again. It should work. foo(1)
Named function expressions are only constant when there is no variable declaration inside the function with the same identifier as I just found out. If you look at the second function where there is var bar, the bar acts like a regular variable. All my tests are on chrome.
but in the second it uses bar with the string as function, which throws an error.
Both of the the functions try to change bar to a string. Only one is successful because the other one is a constant. Both of the functions are the same except one with an extra var.
0

Let me try to elaborate in simple word. In first function, you are trying to modify/redefine/override NFE (Named Function Expression) “bar” that is not permissible. As you have not used “strict mode” that’s why you did not receive run time error. In case you will ON strict mode, you will receive “Uncaught TypeError: Assignment to constant variable” because inside NFE function name treated like a constant so that nobody can override its value.

In second function, “bar” will infer a string value that’s why it throws error because after few line, you are using it as a function. I hope, it make some sense. To double check, we can execute below script: Below script will return “function” and then definition of “bar” function because I have not used strict mode

var foo = function bar() {
console.log(typeof(bar)) ;
bar = "new definition"; // try to redefine
console.log( bar ); 
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.