4
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工作正常,它只是最后一个不存在的功能。在函数定义;之后添加 a可以解决所有问题。baz

我知道排除;会搞砸一些事情,但我确信你不应该把;函数放在后面。没有语言能做到这一点。为什么排除;afterbaz函数会导致 this 中断?我应该把;我的函数定义放在后面吗?

4

3 回答 3

10

Michael Geary 的回答相反,赋值语句不需要分号。

考虑不起眼的立即调用函数表达式:

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

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

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(); },所以 to 的论点function() {}undefined
  6. 我们现在执行function() {}正确。它从不绑定它的参数,所以undefined忽略了。它不返回任何内容,因此调用结果为undefined.
  7. 现在我们已经完成了对该表达式的评估!我们将设置that.baz结果,undefined

希望您现在看到解析器将您立即调用的函数表达式误解为另一个匿名函数的参数,立即调用

如何预防?

如前所述,括号并不是明确引入表达式的唯一方法。事实上,当它们无意中出现在表达式的中间时,可能会导致这样的问题。我们还提到了分配,但我们不想分配一些东西。

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

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

我们用:

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

!只能出现在表达式的开头,因此 JavaScript 开始解析表达式。之后只能有一个表达式!,因此函数被解析为函数表达式而不是函数定义。由于优先级,函数首先被调用。它不返回任何东西,所以它返回undefinedundefined是假的,所以!翻转它并产生true. 因为我们从来没有对这个表达式的结果做任何事情,所以true被丢弃了。(如果我们返回一个真实的值也没关系;然后!会产生false并且会被丢弃。无论如何,它都会被丢弃。)

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


脚注

1 从技术上讲,赋值是一个表达式,而不是一个语句,但对于我们的目的而言,这并不重要。
2除了return, continue, break, 和类似的陈述,它会造成更多的混乱。

于 2013-05-14T02:39:57.293 回答
5

函数声明后不需要分号:

function foo() {
}

如果您添加分号并没有什么坏处,您只是不需要它:

function foo() {
};

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

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

OTOH,您确实需要在赋值语句后使用分号:

that.prop = 1;

如果您分配的值是函数表达式,则不会改变:

that.foo = function() {};
于 2013-05-14T01:45:00.657 回答
0

这也将起作用

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

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

得到它?

它们是两种说法。他们需要两个结局。

于 2013-05-14T01:47:53.153 回答