【问题标题】:Why does this JavaScript named function expression throw an exception with var and not without var为什么这个 JavaScript 命名函数表达式会抛出带有 var 而不是没有 var 的异常
【发布时间】:2018-11-30 20:01:38
【问题描述】:
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 不是函数

富(1)

【问题讨论】:

  • 你得到的究竟是什么“异常”?
  • 您正在将函数 bar 更改为字符串,这就是您看到错误的原因。
  • 第一个,您在全局范围内设置 bar,.. 但是您在本地范围内也有 bar,这将首先看到.. 在第二个中,您创建了两个在本地范围内。如果您将第一个更改为 window.bar,那么您所做的将变得更加明显。
  • @Pointy Uncaught TypeError: bar is not a function.
  • @Keith 我很确定第一个函数在全局范围内没有barbar 是命名函数表达式的标识符,只能在函数内部访问。这就是使问题变得有趣的原因。

标签: javascript


【解决方案1】:

原因是根据 ES 规范,在命名函数的名称上有一个不可变绑定

来自Section 14.1.20 of the ES6 spec

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

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

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

观察:

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

foo();

因此,在您的第一个示例中,您对 bar 的分配基本上没有效果,除非它处于严格模式。

在第二个中,您通过在新范围内声明一个变量来隐藏bar 标识符。这就是为什么bar 保留第二个示例中分配的值的原因。

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

foo();

【讨论】:

  • 我怀疑分配给bar 确实有效果,即隐式创建全局bar。如果函数处于“严格”模式,它可能会抛出错误。
  • @Pointy 我认为它也会创建一个全局变量,但它没有。将其作为严格模式运行,它给了我“TypeError:分配给常量变量”。所以我猜酒吧标识符是一个不能改变的常量值,它不会在非严格模式下抛出错误。
  • 嗯,好的,所以它最终表现得像 const 符号。我没想到,但这很有趣。
  • 这是使用浏览器的问题,在边缘,没有发生任何事情,没有错误/异常。
  • @JLRishe 好的,我终于明白了这个问题的一切。我现在可以去睡觉了。谢谢。
【解决方案2】:

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

为什么第一个函数不抛出异常? bar 明确分配给一个字符串。

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

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

Assignment to constant variable.

这意味着命名函数被实现为常量。

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 中的标识符不能被引用,也不影响包含 FunctionExpression 的范围。”

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

你的问题不清楚。

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

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

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

var a = 42;

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

【讨论】:

  • 对不起,我有一些错别字。尝试再次复制和粘贴。它应该工作。富(1)
  • 命名函数表达式只有在函数内部没有变量声明且标识符与我刚刚发现的相同时才是常量。如果您查看有var bar 的第二个函数,bar 就像一个常规变量。我所有的测试都在 chrome 上。
  • 但在第二个中它使用带有字符串的 bar 作为函数,这会引发错误。
  • 这两个函数都尝试将bar 更改为字符串。只有一个是成功的,因为另一个是常数。除了多了一个var之外,这两个函数都是一样的。
【解决方案3】:

让我试着用简单的词来详细说明。 在第一个函数中,您试图修改/重新定义/覆盖不允许的 NFE(命名函数表达式)“bar”。由于您没有使用“严格模式”,这就是您没有收到运行时错误的原因。如果您将开启严格模式,您将收到“Uncaught TypeError: Assignment to constant variable”,因为在 NFE 内部函数名称被视为常量,因此没有人可以覆盖它的值。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-30
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多