为什么此JavaScript命名函数表达式会引发带有var的异常,而不是没有var

时间:2018-06-21 13:06:51

标签: javascript

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

以上功能没有错误。

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

var添加到bar后,第二个函数出错。

我希望两个函数都抛出异常,而不仅仅是带有var bar的第二个函数。

我不明白为什么只有第二个函数引发异常。

我得到带有var的变量声明不会覆盖标识符“ bar”,但是赋值将在运行时执行。我明白为什么var bar是字符串而不是第二个函数上的函数,因此引发异常。

为什么第一个函数不会引发异常? bar显然已分配给字符串。

我读了documentation,发现下面有一些有用的内容。

  

可以从FunctionExpression的FunctionBody内部引用FunctionExpression中的标识符,以允许该函数以递归方式调用自身。但是,与FunctionDeclaration中的功能不同,FunctionExpression中的Identifier不能从其引用,也不会影响包围FunctionExpression的作用域。

“无法引用FunctionExpression中的标识符”是否意味着我无法在第一个函数中使用bar = "change bar reference";

JavaScript脚本引擎看到bar = "change bar reference"时会做什么?它只是跳过行吗?

  

编辑:未捕获的TypeError:bar不是函数

foo(1)

3 个答案:

答案 0 :(得分:2)

这样做的原因是,根据ES规范,命名函数的名称上存在不可变绑定

来自Section 14.1.20 of the ES6 spec

  
      
  1. envRec 成为funcEnv的EnvironmentRecord。
  2.   
  3. name 为BindingIdentifier的StringValue。
  4.   
  5. 执行envRec.CreateImmutableBinding(name)。
  6.   

不可变的绑定意味着标识符的值不能被覆盖。此外,尝试以严格模式should produce a runtime error分配给它:

  

12.14.1:在ECMAScript 2015中,包含对不可变绑定的分配(例如FunctionExpression的函数名称)的严格模式代码不会产生早期错误。而是会产生运行时错误。

观察:

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

foo();

因此,在第一个示例中,除非严格模式,否则您对bar的分配实际上没有任何作用。

在第二个示例中,通过在新作用域中声明变量来使bar标识符 shadowing 。这就是bar在第二个示例中保留分配值的原因。

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

foo();

答案 1 :(得分:1)

至少在调用foobar时,您会收到错误消息,因为该函数已不存在。

  

为什么第一个函数没有抛出异常? bar显然已分配给字符串。

在运行时,没有调用该函数,什么也没有发生,但是在调用该函数之后,变量bar以字符串作为值,而不是用于进一步调用超时的函数。

这取决于。在Edge上,它只是跳过分配,在Chrome上,它会引发错误

Assignment to constant variable.

这意味着命名函数被实现为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);

我阅读了文档,并在下面得到了一些有用的信息。 “可以从FunctionExpression的FunctionBody内部引用FunctionExpression中的标识符,以允许该函数以递归方式调用其自身。但是,与FunctionDeclaration中的情况不同,FunctionExpression中的Identifier不能从包含该FunctionExpression的范围中进行引用,并且不会对其产生影响。”

  

是否“无法从FunctionExpression中引用标识符”是否意味着我不能做bar =“更改栏引用”;在第一个功能?

您的问题不清楚。

  

当JavaScript脚本引擎看到bar =“更改栏引用”时会做什么?它只是跳过行吗?

不。它将值分配给本地bar或全局bar,但是如果具有本地或全局变量,则将本地变量用于全局变量。

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

var a = 42;

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

答案 2 :(得分:0)

让我尝试用简单的词来阐述。 在第一个函数中,您试图修改/重新定义/覆盖不允许的NFE(命名函数表达式)“ bar”。由于您没有使用“严格模式”,因此没有收到运行时错误。万一您将进入严格模式,您将收到“ Uncaught TypeError:Assigning to constant variable”,因为在NFE函数名内部将其视为常量,因此任何人都无法覆盖其值。

在第二个函数中,“ bar”将推断出一个字符串值,这就是为什么它会引发错误,因为几行之后,您就将其用作函数。 我希望这是有道理的。 要仔细检查,我们可以执行以下脚本: 下面的脚本将返回“ function”,然后返回“ bar” function的定义,因为我没有使用严格模式

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