简体   繁体   English

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

[英]Javascript undefined class function when missing semicolon

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 works fine, it's only the last function that doesn't exist. that.bar工作正常,它只是最后一个不存在的函数。 Adding a ; 添加; after the baz function definition fixes everything. baz函数定义之后修复了所有内容。

I know excluding ; 我知道排除; can mess some things up, but I thought for sure you're not supposed to put ; 可以弄乱一些东西,但我想你肯定不应该放; after functions. 功能之后。 No language does that. 没有语言可以做到。 Why does excluding the ; 为什么要排除; after the baz function cause this to break? baz函数导致这个打破之后? Should I be putting ; 我应该放; after my function definitions? 在我的函数定义之后?

Contrary to Michael Geary's answer , assignment statements do not require semicolons. 相反, 迈克尔·吉尔的回答 ,赋值语句不需要分号。

Consider the humble immediately-invoked function expression: 考虑一下简单的立即调用的函数表达式:

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

What are the extra parentheses wrapping the function() { ... } for? 包含function() { ... }的额外括号是什么? They're to prevent it from being interpreted as a function definition , forcing it to be interpreted as a function expression. 它们是为了防止它被解释为函数定义 ,迫使它被解释为函数表达式。 It's basically equivalent to this, but without creating another variable: 它基本上等同于此,但不创建另一个变量:

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

But parentheses are only one way to introduce something that must be an expression. 但括号只是引入必须表达的东西的一种方式。 In particular, an initializer of an assignment statement will introduce an expression 1 : 特别是, 赋值语句的初始值设定项将引入表达式 1

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

Clearly, composing these concepts shows that in this: 显然,撰写这些概念表明:

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

The wrapping parentheses are not actually needed! 实际上并不需要包裹括号! We could just as easily write this because the function() cannot be interpreted as a function definition: 我们可以很容易地写这个,因为function()不能被解释为函数定义:

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

So what does this have to do with semicolons? 那么这与分号有什么关系呢? It turns out that if the next line can continue the statement or expression, it will. 事实证明,如果下一行可以继续语句或表达式,它将会。 2 You've written this: 2你写的这个:

that.baz = function() {}

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

With this knowledge, you know it's actually being interpreted as this: 有了这些知识,你知道它实际上被解释为:

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

That's a bit tricky, so here's what's happening: 这有点棘手,所以这就是发生的事情:

  1. First, function() {} is evaluated ( not executed), yielding a function object. 首先,评估( 执行) function() {} ,产生一个函数对象。
  2. This function object is then called, but wait ! 然后调用此函数对象, 但等待 It has some arguments we have to evaluate before we can begin executing it proper. 在我们开始正确执行之前,我们必须先评估一些参数。
  3. The only argument is the expression 唯一的论点是表达式

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

    That's obviously an immediately-invoked function expression, so we'll begin executing it. 这显然是一个立即调用的函数表达式,所以我们将开始执行它。

  4. We try to find that.baz and call it, but it fails because that.baz does not exist and thus resolves to undefined ! 我们试图找到that.baz并调用它,但它失败了,因为that.baz不存在,因此解析为undefined Oh no! 不好了! The JavaScript engine stops here because an uncaught exception was thrown, but for clarity, let's pretend it went on. JavaScript引擎在这里停止,因为抛出了未捕获的异常,但为了清楚起见,让我们假装它继续。
  5. We never returned anything from function() { that.baz(); } 我们从来没有从function() { that.baz(); } function() { that.baz(); } , so the argument to function() {} is undefined . function() { that.baz(); } ,所以参数function() {}undefined
  6. We now execute function() {} proper. 我们现在正确执行function() {} It never binds its argument, so the undefined is ignored. 它永远不会绑定它的参数,因此忽略undefined It doesn't return anything, so the call results in undefined . 它不返回任何内容,因此调用导致undefined
  7. Now we're done evaluating that expression! 现在我们已经完成了对表达式的评估! We'll set that.baz to the result, undefined . 我们将that.baz设置为结果, undefined

Hopefully you see now that the parser misinterpreted your immediately-invoked function expression as, well, an argument to another anonymous function, immediately invoking it . 希望您现在看到解析器误将您的立即调用的函数表达式误解为另一个匿名函数的参数,并立即调用

How to prevent it? 怎么预防呢?

As mentioned previously, parentheses are not the only way to unambiguously introduce an expression. 如前所述,括号不是明确引入表达式的唯一方法。 In fact, when they're in the middle of the expression unintentionally, it can cause problems like this. 事实上,当他们无意中处于表达的中间时,它可能会导致这样的问题。 We also mentioned assignments, but we don't want to assign something. 我们还提到了作业,但我们不想分配一些东西。

What's left? 还剩什么? Unary operators. 一元运营商。 There's a few we can use. 我们可以使用一些。 I like ! 我喜欢! , so we'll use that. 所以我们会用它。 Rather than using: 而不是使用:

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

We use: 我们用:

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

The ! ! can only come at the start of an expression, so JavaScript starts parsing an expression. 只能在表达式的开头出现,因此JavaScript开始解析表达式。 Only an expression can come after ! 只有一个表达才可以追随! , so the function is parsed as a function expression rather than a function definition. ,因此该函数被解析为函数表达式而不是函数定义。 Because of precedence, the function is called first. 由于优先级,首先调用该函数。 It doesn't return anything, so it returns undefined . 它不返回任何内容,因此返回undefined undefined is falsy, so ! undefined是假的,所以! flips it and yields true . 翻转它并产生true Since we never did anything with the result of this expression, the true is discarded. 由于我们从未对此表达式的结果执行任何操作,因此将丢弃true (It wouldn't have mattered if we returned a truthy value either; then ! would have yielded false and that would be discarded. No matter what, it'll be discarded.) (如果我们返回一个真正的值,那就不重要了;那么!会产生false而且会被丢弃。无论如何,它都会被丢弃。)

TLDR: Use a unary operator rather than parentheses for IIFEs. TLDR:对IIFE使用一元运算符而不是括号。


Footnotes 脚注

1 Technically , assignment is an expression, not a statement, but for our purposes it does not matter. 1 从技术上讲 ,赋值是一种表达,而不是一种陈述,但对于我们的目的而言,它并不重要。
2 With the exception of return , continue , break , and similar statements where it would confuse much more confusion. 2随着外returncontinuebreak ,和类似表述它会混淆更加混乱。

You don't need a semicolon after a function declaration: 函数声明后不需要分号:

function foo() {
}

There is no harm done if you do add a semicolon, you just don't need it: 如果你添加分号没有任何伤害,你只需要它:

function foo() {
};

That inserts an empty statement after your function declaration, which has no effect. 在函数声明后插入一个空语句,该语句无效。 In the same way, this would do no harm, other than confusing people who read your code: 以同样的方式,除了让阅读代码的人感到困惑之外,这不会造成任何伤害:

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

OTOH, you do need a semicolon after an assignment statement: OTOH, 需要一个赋值语句后分号:

that.prop = 1;

and that doesn't change if the value you're assigning is a function expression: 如果您分配的值是函数表达式,则不会更改:

that.foo = function() {};

This will also work 这也行

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

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

Get it? 得到它?

They are two statements. 他们是两个陈述。 They require two endings. 他们需要两个结局。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM