函数是一段有组织、可复用的代码,用于执行某个操作。在 JavaScript 中,定义函数有多种方式。这里我们会了解_函数声明_和_函数表达式_。其他方式,比如箭头函数,会在其他概念中介绍。
在 JavaScript 中,定义函数的标准方式是_函数声明_,也叫_函数定义_或_函数语句_。
它由function关键字、函数名,以及用圆括号括起来、以逗号分隔的形参列表组成。之后是函数体(一组定义函数做什么的语句),用大括号包起来。
function someName(param1, param2, param3) {
// ...
}
在 JavaScript 中,调用函数的方法是写出函数名,后面跟上包含实参的圆括号。
someName(arg1, arg2, arg3);
在 JavaScript 中,仅仅写出函数名并不会调用函数。
function sayHello() {
console.log('Hello, World!');
}
sayHello;
// => [Function: sayHello]
// The text representations varies between environments.
sayHello();
// => 'Hello, World!'
在函数体中使用形参时,要留意可能对传入函数的原始值造成的副作用。在 JavaScript 中,实参是原始值引用的一个副本。这在实践中意味着什么,取决于实参的数据类型。
原始数据类型的值是_不可变的_。 所有的“修改”都会创建一个新的原始值。 因此,函数体中对实参所做的任何操作都不会影响原始值。
const num = 0;
function increase(number) {
number = number + 1;
}
increase(num);
num;
// => 0
对于_对象_、数组、_函数_这样的值来说,情况就不同了。 由于复制的是引用,重新赋值不会影响原始值。 不过,由于你处理的是浅拷贝,修改函数体中的实参也会改变传入的原始值。
const nums = [0, 1, 2, 3, 4];
function pushFive(arr) {
arr.push(5);
}
pushFive(nums);
nums;
// => [0, 1, 2, 3, 4, 5]
默认情况下,在 JavaScript 中,函数声明里定义的所有形参都是可选的。如果传给函数的实参比形参少,缺失的实参在函数体中会是undefined,参见 null 和 undefined。在很多情况下,如果没有传入值,或传入的值是undefined,用_默认值_初始化形参往往更合理。这可以通过在函数定义中直接指定默认形参来实现。
function someName(param1 = defaultValue1, param2 = defaultValue2) {
// ...
}
调用函数时传入的实参可以比函数定义中的形参多。所有实参,包括多出来的那些,都能在 arguments “数组”中找到。
也可以定义接受任意数量实参的函数(可变参数函数),更多细节参见剩余与展开运算符中的剩余形参。
return语句return语句会结束函数的执行,并指定一个返回给函数调用方的值。一个函数可以有多个return语句。
function checkNumber(num) {
if (num === 0) {
return 'You passed 0, please provide another number.';
}
return 'Thanks for passing such a nice number.';
}
一个不返回值、或没有return语句的函数,其结果是undefined。JavaScript 中没有隐式的return。
function nakedReturn(a) {
a * 2;
return;
}
nakedReturn(1);
// => undefined
function noReturn(a) {
a * 2;
}
noReturn(1);
// => undefined
在 JavaScript 中,你只能返回一个值。如果你想传递更多信息,需要先把它们组合成一个整体,通常是对象或数组。
function divide(a, b) {
return {
quotient: Math.floor(a / b),
remainder: a % b,
};
}
函数声明是一条独立的语句。但有时把函数定义为另一个表达式的一部分会很有用,比如放在赋值中、作为函数的形参(回调),或作为对象中的值。这可以用函数表达式来实现。它的语法与函数声明相同,只是可以省略函数名来创建一个_匿名函数_。
const someFunction = function (param) {
// ...
};
someOtherFunction(function (param) {
// ...
});
const obj = {
someFunction: function (param) {
// ...
},
};
函数会在 JavaScript 中引入一个新的执行上下文。在函数内部定义的变量,在该函数外部无法访问。但定义在父级作用域(即函数自身被定义时所在的作用域)中的变量,在函数内部可以访问。MDN 关于作用域的文档展示了这种行为的例子。
想进一步了解 JavaScript 中的变量作用域,可以看看闭包。