函数是一段有组织、可复用的代码,用来执行某个动作。在 JavaScript 中定义函数有多种方式。这里我们会看_函数声明_和_函数表达式_。其他形式,比如箭头函数,会在其他概念中介绍。
在 JavaScript 中,定义函数的标准方式是_函数声明_,也叫_函数定义_或_函数语句_。
它由function关键字、函数名,以及圆括号里用逗号分隔的形参列表组成。后面是函数体(定义函数行为的一组语句),用大括号包裹。
function someName(param1, param2, param3) {
// ...
}
在 JavaScript 中,调用函数的方法是写出函数名,后面跟一对括号,括号里放实参。
someName(arg1, arg2, arg3);
在函数体中使用形参时,要留意可能出现的副作用。
在 JavaScript 中,函数声明里定义的所有形参默认都是可选的。
如果你提供的实参比形参少,缺少的实参在函数内部就是undefined,参见 Null 与 Undefined。
很多时候,指定一个比undefined更合适的默认值是合理的。
这可以在函数定义中直接指定默认形参来实现。
function someName(param1 = defaultValue1, param2 = defaultValue2) {
// ...
}
return语句会结束函数的执行,并指定一个要返回给函数调用者的值。一个函数可以有多个return语句。
function checkNumber(num) {
if (num === 0) {
return 'You passed 0, please provide another number.';
}
return 'Thanks for passing such a nice number.';
}
函数的返回值可以保存在变量中。
function sum(x, y) {
return x + y;
}
const total = sum(5, 10);
// => 15
如果函数没有return任何值,或者没有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) {
// ...
},
};
在这个练习中,你要再写一些代码,用来准备和烹饪你最爱的食谱书里的美味千层面。
你有 5 个任务。 第一个和烹饪本身有关,其余 4 个都和完美的准备工作有关。
千层面在烤箱里的时候,你想知道是不是已经可以把它拿出来了。 为了确保千层面不会在烤箱里烤糊,你通常会设一个定时器。 但有时候你会忘了这回事。
写一个函数 cookingStatus,它接受定时器上剩余的时间(分钟)作为形参。
这个函数有 3 种可能的结果。
0,它应该返回 'Lasagna is done.'。'Not done, please wait.'。'You forgot to set the timer.'。定时器显示的值永远不会低于 0。
cookingStatus(12);
// => 'Not done, please wait.'
cookingStatus();
// => 'You forgot to set the timer.'
对于你接下来要准备的千层面,你想确保留出足够的时间,好尽情享受烹饪的过程。 你已经为千层面要用的所有层列好了计划。 现在你想据此估算准备要花多长时间。
实现一个函数 preparationTime,它接受一个层的数组,以及每层平均的准备时间(分钟)。
这个函数应该根据层数返回对总准备时间的估算值。
如果调用函数时没有提供平均准备时间,就假设为 2 分钟。
const layers = ['sauce', 'noodles', 'sauce', 'meat', 'mozzarella', 'noodles'];
preparationTime(layers, 3);
// => 18
preparationTime(layers);
// => 12
除了留出时间,你还想确保有足够的酱汁和面条,来做出你梦想中的千层面。 千层面里每有一层面条,你就需要 50 克面条。 每有一层酱汁,你就需要 0.2 升酱汁。
定义函数 quantities,它接受一个层的数组作为形参。
这个函数随后会算出做这顿饭所需的面条和酱汁用量。
结果应该以一个对象的形式返回,键为noodles和sauce。
quantities(['sauce', 'noodles', 'sauce', 'meat', 'mozzarella', 'noodles']);
// => { noodles: 100, sauce: 0.4 }
不久前,你去一位朋友家做客,在那儿吃了千层面。 那千层面非常棒,而且有种特别的风味。 朋友把配料清单发给了你,还告诉你清单上的最后一项就是让这顿饭如此特别的“秘密配料”。 现在你也想把这种秘密配料加进自己的食谱里。
写一个函数 addSecretIngredient,它接受两个配料数组作为形参。
第一个形参是朋友发给你的清单,第二个是你自己食谱的配料清单。
这个函数应该把朋友清单上的最后一项添加到你的清单末尾。
表示你食谱的那个数组应该被直接修改,而函数不应该返回任何东西。
不过,第一个实参不应该被修改。
const friendsList = ['noodles', 'sauce', 'mozzarella', 'kampot pepper'];
const myList = ['noodles', 'meat', 'sauce', 'mozzarella'];
addSecretIngredient(friendsList, myList);
// => undefined
console.log(myList);
// => ['noodles', 'meat', 'sauce', 'mozzarella', 'kampot pepper']
你食谱书上列出的用量只够做 2 份千层面。 既然你下次想给更多人做饭,你就想算出不同份数分别需要的用量。
实现一个函数 scaleRecipe,它接受两个形参。
这个函数应该返回一个食谱对象,里面是所需份数对应的用量。 不过,你想保留原来的食谱。 也就是说,在这个任务里,食谱这个实参不应该被修改。
const recipe = {
noodles: 200,
sauce: 0.5,
mozzarella: 1,
meat: 100,
};
scaleRecipe(recipe, 4);
// =>
// {
// noodles: 400,
// sauce: 1,
// mozzarella: 2,
// meat: 200,
// };
console.log(recipe);
// =>
// {
// noodles: 200,
// sauce: 0.5,
// mozzarella: 1,
// meat: 100,
// };