在 JavaScript 中,函数不是一种“神奇的语言结构”,而是一种特殊的值。

我们之前使用的语法称为函数声明(Function Declaration):
function sayHi() { alert( "Hello" ); }
还有一种创建函数的语法,称为函数表达式(Function Expression)。
JavaScript 匿名函数简介
匿名函数是没有名称的函数。它允许我们在任何表达式中间创建一个新函数。下面展示了如何定义一个简单的匿名函数:
例如:
let sayHi = function() { alert( "Hello" ); };
在这里,我们可以看到变量 sayHi
获取了一个值——一个通过 function() { alert("Hello"); }
创建的新函数。
由于函数是在赋值表达式的上下文中创建的(等号右侧),因此这是一个函数表达式(Function Expression)。请注意,如果您不将匿名函数放在 ()
中,则会出现语法错误。()
使匿名函数成为返回函数对象的表达式。
匿名函数在创建后不可访问。因此,您经常需要将其分配给一个变量。请注意,function
关键字后面没有函数名。对于函数表达式来说,省略名称是允许的。
我们在这里立即将它赋值给变量,因此这些代码的含义是一样的:“创建一个函数,并将其放入变量 sayHi
中”。
在我们稍后会遇到的一些更高级的场景中,函数可能会被创建后立即调用,或者被安排稍后执行,而不会被存储到任何地方,因此保持匿名。这样的函数,成为匿名函数。
函数是一个值
让我们再强调一遍:无论函数是如何创建的,函数本质上都是一个值。上面两个示例都将函数存储在变量 sayHi
中。
我们甚至可以使用 alert
打印出这个值:
function sayHi() { alert( "Hello" ); } alert( sayHi ); // shows the function code
请注意,最后一行并不会执行函数,因为 sayHi
后面没有括号。有些编程语言中,提到函数名就会自动执行函数,但 JavaScript 并不是这样。
在 JavaScript 中,函数是一个值,因此我们可以像处理其他值一样处理它。上面的代码展示的是函数的字符串表示形式,也就是它的源代码。
当然,函数是一种特殊的值,因为我们可以像这样调用它:sayHi()
。
但它依然是一个值,所以我们可以像对待其他类型的值一样使用它。
我们可以将一个函数复制到另一个变量中:
function sayHi() { // (1) create alert( "Hello" ); } let func = sayHi; // (2) copy func(); // Hello // (3) run the copy (it works)! sayHi(); // Hello // this still works too (why wouldn't it)
上面的代码详细说明如下:
-
第 (1) 行是函数声明,它创建了一个函数,并将其放入名为
sayHi
的变量中。 -
第 (2) 行将这个函数复制到变量
func
中。再次注意:sayHi
后面没有括号。如果加了括号,比如func = sayHi()
,那么func
得到的将是sayHi()
的返回结果,而不是函数本身。 -
现在我们可以通过
sayHi()
或func()
来调用这个函数。
我们也可以在第一行使用函数表达式来声明 sayHi
:
let sayHi = function() { // (1) create alert( "Hello" ); }; let func = sayHi; //(2) // ...
一切都会照常运行。
为什么需要分号?
你可能会想,为什么函数表达式(Function Expressions)末尾有分号 ;
,而函数声明(Function Declarations)没有:
function sayHi() { // ... } let sayHi = function() { // ... };
答案很简单:函数表达式是在赋值语句中创建的,形式为 function(…) {…}
,例如 let sayHi = …;
。分号 ;
是语句结尾的推荐写法,它不是函数语法的一部分。
对于更简单的赋值,比如 let sayHi = 5;
,也需要分号,函数赋值时同样需要分号。
回调函数
让我们来看更多将函数作为值传递并使用函数表达式的示例。
我们将编写一个名为 ask(question, yes, no)
的函数,带有三个参数:
-
question
:问题的文本 -
yes
:如果回答是“是”,执行的函数 -
no
:如果回答是“否”,执行的函数
这个函数应该提出问题,并根据用户的回答,调用 yes()
或 no()
:
function ask(question, yes, no) { if (confirm(question)) yes() else no(); } function showOk() { alert( "You agreed." ); } function showCancel() { alert( "You canceled the execution." ); } // usage: functions showOk, showCancel are passed as arguments to ask ask("Do you agree?", showOk, showCancel);
在实践中,这种函数非常有用。现实生活中的询问与上面的例子之间的主要区别在于,现实生活中的函数使用比简单的确认框更复杂的方式与用户交互。在浏览器中,这类函数通常会显示一个漂亮的提问窗口。但那是另一个话题。
ask
函数的 showOk
和 showCancel
参数被称为回调函数(callback functions)或简称回调。
其思路是我们传递一个函数,并期望在必要时它能“回调”我们。在我们的例子中,showOk
是“是”答案的回调,showCancel
是“否”答案的回调。
我们可以使用函数表达式来编写一个等效的、更简短的函数:
function ask(question, yes, no) { if (confirm(question)) yes() else no(); } ask( "Do you agree?", function() { alert("You agreed."); }, function() { alert("You canceled the execution."); } );
wer
