缺少分号时Javascript未定义类函数

时间:2013-05-14 01:42:16

标签: javascript

function Foo() {
    var that = this;
    that.bar = function() {}
    that.baz = function() {}

    (function() {
        that.baz();
    }());
}
new Foo;

Uncaught TypeError: Object #<Foo> has no method 'baz'

that.bar工作正常,它只是最后一个不存在的函数。在;函数定义之后添加baz可修复所有内容。

我知道排除;可能会弄乱一些事情,但我确信你不应该把;放在功能之后。没有语言可以做到。为什么在;函数之后排除baz会导致此错误?我应该在我的函数定义之后放置;吗?

3 个答案:

答案 0 :(得分:9)

Michael Geary's answer相反,作业语句 需要分号。

考虑一下简单的立即调用的函数表达式:

(function() { /* do something */ })();

包裹function() { ... }的额外括号是什么?它们是为了防止它被解释为函数定义,迫使它被解释为函数表达式。它基本上等同于此,但没有创建另一个变量:

var temp = function() { /* do something */ };
temp();

但括号只是引入必须表达的东西的一种方式。特别是,赋值语句的初始值设定项将引入表达式 1

someVar = /* what goes here must be an expression */;

显然,撰写这些概念表明:

someVar = (function() { return 5; })();

实际上并不需要包裹括号!我们可以轻松地写这个,因为function()不能被解释为函数定义:

someVar = function() { return 5; } ();

那么这与分号有什么关系呢?事实证明,如果下一行可以继续语句或表达式,它将会。 2 你写的是:

that.baz = function() {}

(function() {
    that.baz();
}());

有了这些知识,你知道它实际上被解释为:

that.baz = (function() {})(function() {
    that.baz();
}());

这有点棘手,所以这就是发生的事情:

  1. 首先,评估function() {}执行),产生一个函数对象。
  2. 然后调用此函数对象,但等待!在我们开始正确执行它之前,我们必须先评估一些参数。
  3. 唯一的参数是表达式

    function() {
        that.baz();
    }()
    

    这显然是一个立即调用的函数表达式,所以我们将开始执行它。

  4. 我们尝试找到that.baz并调用它,但它失败了,因为that.baz不存在,因此解析为undefined!不好了! JavaScript引擎在这里停止,因为抛出了未捕获的异常,但为了清楚起见,让我们假装它继续。
  5. 我们从未从function() { that.baz(); }返回任何内容,因此function() {}的参数为undefined
  6. 我们现在正确执行function() {}。它永远不会绑定它的参数,因此忽略undefined。它不会返回任何内容,因此调用会生成undefined
  7. 现在我们已经完成了对该表达式的评估!我们会将that.baz设置为结果undefined
  8. 希望您现在看到解析器误解了您的立即调用的函数表达式,因为它是另一个匿名函数的参数,立即调用 it

    如何预防?

    如前所述,括号不是明确引入表达式的唯一方法。事实上,当他们无意中处于表达的中间时,它可能会导致这样的问题。我们还提到了作业,但我们不想分配内容。

    还剩下什么? 一元运营商。 我们可以使用一些。我喜欢!,所以我们会用它。而不是使用:

    (function() {
        that.baz();
    }());
    

    我们使用:

    !function() {
        that.baz();
    }();
    

    !只能出现在表达式的开头,因此JavaScript开始解析表达式。只有表达式可以在!之后出现,因此该函数被解析为函数表达式而不是函数定义。由于优先级,首先调用该函数。它不返回任何内容,因此返回undefinedundefined是假的,所以!翻转它并产生true。由于我们从未对此表达式的结果执行任何操作,因此true将被丢弃。 (如果我们返回一个真正的值,它就不重要;然后!会产生false并且会被丢弃。无论如何,它都会被丢弃。)

    TLDR:使用一元运算符而不是IIFE的括号。


    脚注

    1 技术上,赋值是表达式,而不是声明,但对于我们的目的而言,它无关紧要。
    2 returncontinuebreak以及类似的陈述除外,它会使混淆更多。

答案 1 :(得分:5)

函数声明后不需要分号:

function foo() {
}

如果你添加分号,没有任何伤害,你只需要它:

function foo() {
};

在函数声明后插入一个空语句,该语句无效。以同样的方式,这不会造成伤害,除了让阅读代码的人感到困惑:

var i = 1;;;;;;;;;;;;;;;;;;;;;;;;;;

OTOH,你在转让声明后需要一个分号:

that.prop = 1;

如果您指定的值是函数表达式,则不会更改:

that.foo = function() {};

答案 2 :(得分:0)

这也可行

function Foo() {
    var that = this;
    { that.bar = function() {} }
    { that.baz = function() {} }

    (function() {
        that.baz();
    }());
}
new Foo;

得到它?

他们是两个陈述。他们需要两个结局。