手机店的店员可能不会拿着一个计算器到处走,用它来搞清税费和最终的购物款。这是一个她需要定义一次然后一遍又一遍地重用的任务。很有可能的是,公司有一个带有内建这些“功能”的收银机(电脑,平板电脑,等等)。
相似地,几乎可以肯定你的程序想要将代码的任务分割成可以重用的片段,而不是频繁地多次重复自己。这么做的方法是定义一个function
。
一个函数一般来说是一段被命名的代码,它可以使用名称来被“调用”,而每次调用它内部的代码就会运行。考虑如下代码:
function printAmount() {
console.log( amount.toFixed( 2 ) );
}
var amount = 99.99;
printAmount(); // "99.99"
amount = amount * 2;
printAmount(); // "199.98"
函数可以选择性地接收参数值(也就是参数)—— 你传入的值。而且它们还可以选择性地返回一个值。
function printAmount(amt) {
console.log( amt.toFixed( 2 ) );
}
function formatAmount() {
return "$" + amount.toFixed( 2 );
}
var amount = 99.99;
printAmount( amount * 2 ); // "199.98"
amount = formatAmount();
console.log( amount ); // "$99.99"
函数printAmount(..)
接收一个参数,我们称之为amt
。函数formatAmount()
返回一个值。当然,你也可以在同一个函数中组合这两种技术。
函数经常被用于你打算多次调用的代码,但它们对于仅将有关联的代码组织在一个命名的集合中也很有用,即便你只打算调用它们一次。
考虑如下代码:
const TAX_RATE = 0.08;
function calculateFinalPurchaseAmount(amt) {
// 计算带有税费的新费用
amt = amt + (amt * TAX_RATE);
// 返回新费用
return amt;
}
var amount = 99.99;
amount = calculateFinalPurchaseAmount( amount );
console.log( amount.toFixed( 2 ) ); // "107.99"
虽然calculateFinalPurchaseAmount(..)
只被调用了一次,但是将它的行为组织进一个分离的带名称的函数,让使用它逻辑的代码(amount = calculateFinal...
语句)更干净。如果函数中拥有更多的语句,这种好处将会更加明显。
作用域
如果你向手机店的店员询问一款她们店里没有的手机,那么她就不能卖给你你想要的。她只能访问她们店库房里的手机。你不得不到另外一家店里去看看能不能找到你想要的手机。
编程对这种概念有一个术语:作用域(技术上讲称为 词法作用域)。在JavaScript中,每个函数都有自己的作用域。作用域基本上就是变量的集合,也是如何使用名称访问这些变量的规则。只有在这个函数内部的代码才能访问这个函数 作用域内 的变量。
在同一个作用域内变量名必须是唯一的 —— 不能有两个不同的变量a
并排出现。但是相同的变量名a
可以出现在不同的作用域中。
function one() {
// 这个 `a` 仅属于函数 `one()`
var a = 1;
console.log( a );
}
function two() {
// 这个 `a` 仅属于函数 `two()`
var a = 2;
console.log( a );
}
one(); // 1
two(); // 2
另外,一个作用域可以嵌套在另一个作用域中,就像生日Party上的小丑在一个气球的里面吹另一个气球一样。如果一个作用域嵌套在另一个中,那么在内部作用域中的代码就可以访问这两个作用域中的变量。
考虑如下代码:
function outer() {
var a = 1;
function inner() {
var b = 2;
// 我们可以在这里同时访问 `a` 和 `b`
console.log( a + b ); // 3
}
inner();
// 我们在这里只能访问 `a`
console.log( a ); // 1
}
outer();
词法作用域规则说,在一个作用域中的代码既可以访问这个作用域中的变量,又可以访问任何在它外面的作用域的变量。
所以,在函数inner()
内部的代码可以同时访问变量a
和b
,但是仅在outer()
中的代码只能访问a
—— 它不能访问b
因为这个变量仅存在于inner()
内部。
回忆一下先前的这个代码段:
const TAX_RATE = 0.08;
function calculateFinalPurchaseAmount(amt) {
// 计算带有税费的新费用
amt = amt + (amt * TAX_RATE);
// 返回新费用
return amt;
}
因为词法作用域,常数TAX_RATE
(变量)可以从calculateFinalPurchaseAmount(..)
函数中访问,即便它没有被传入这个函数。
注意: 关于词法作用域的更多信息,参见本系列的 作用域与闭包 的前三章。