有两种方法可以在JavaScript中创建函数,即“函数声明”和“函数表达式”。我相信这是Doug Crockford最好的解释,他指出,除非“函数”是给定行上的第一组字符,否则您正在执行函数表达式(不是声明)。
功能声明是挑剔的生物。当你看到它们时你会认出它们。他们是这样的:
function foo() { /* ... */ }
他们总是一个名字(它的报应)和名字被局部范围在其下函数声明为词法环境。所以如果你在全局上下文中执行一个函数声明,那么这个函数可以通过它的全局名称被引用。如果你在一个函数中执行它,那么该函数的名字只能在该函数中被引用,并且在该函数中声明的任何函数都可以被引用。
我认为这种声明函数的方法中最重要的方面就是函数初始化会被提升到当前词法上下文的顶部。因此,你应该永远,永远使用函数声明中的条件,比如这个:
//DON'T DO THIS!
if (x) {
function foo() { return 1; }
} else {
function foo() { return 2; }
}
foo(); //will always be 2, regardless of the value of x.
函数表达式略有不同。通常情况下,他们直接分配给一个变量,像这样:
var foo = function() { /* ... */ };
这是几乎相同的函数声明,除了上述初始化不悬挂。所以你可以做到以下几点:
var foo;
if (x) {
foo = function() { return 1; };
} else {
foo = function() { return 2; };
}
foo(); //will be 1 or 2, depending on the truthy-ness of x.
那么,回到原来的问题。函数表达式也可以有一个名称,尽管它不是必需的,并且它没有作用于声明函数的上下文(与函数声明一样)。相反,它将范围限定在函数自己的词汇上下文中。这在某些情况下非常有用。我个人最喜欢的是这个模式:
(function foo() {
//Do something.
setTimeout(foo, 1000);
}());
foo; //undefined
因为单词“功能”前的括号,这是一个函数表达式和名称仅供内部作用域。但没关系,因为我们只需要在内部调用它(通过setTimeout()
)。结果是该函数将立即执行一次,然后在执行完成后每秒钟左右重新执行一次。这比使用setInterval()
更安全,因为它会在重新计划自身之前一直等待执行完毕,从而防止可能导致错误执行和/或“支配”JavaScript线程的重叠。
从本质上讲,命名函数表达式的使用是有限的,但是当你需要它时,它是非常强大的。
你的名字和你的地址有什么区别? –
除了名称/地址类比之外,实际上可以在bar()的作用域内调用bar(),但在bar()的作用域外,必须使用foo()。 – jeremy
@HotLicks - 这种比喻毫无意义。 'foo'和'bar'在这里都是清晰的名字。他们都提到相同的*地址*。这只是在新功能的范围内才可用。 –