一日,你查看你的程序代码,你有两大块代码看起来几乎完全的一样。事实上它们就是完全一样,除了一个代码里说的是“Spaghetti(意大利面条)”,另一个代码里说的是“Chocolate Moose(巧克力慕丝)”。
// 一个小例子:
alert("I'd like some Spaghetti!");
alert("I'd like some Chocolate Moose!");
这个例子恰好是用Javascript写的,但即使是你不懂Javascript,你也应该能看懂我说的。
当然,重复的代码看起来不太好。所以你决定写一个函数:
function SwedishChef( food )
{
alert("I'd like some " + food + "!");
}
SwedishChef("Spaghetti");
SwedishChef("Chocolate Moose");
没错,这个例子很简单,但你可以想出一些更有实际价值的例子。这样做是更好一些,有很多理由,这些理由估计你都听说过一万遍了。可维护性,可读性,抽象 = 好!
现在,你又发现两块代码几乎完全一样,除了一块是不停的调用一个叫BoomBoom的函数,而一块是不停的调用一个叫PutInPot的函数。除此之外,这两块代码完全一样。
alert("get the lobster");
PutInPot("lobster");
PutInPot("water");
alert("get the chicken");
BoomBoom("chicken");
BoomBoom("coconut");
现在,你需要一个途径,把一个参数传递到一个函数里,而这个参数本身是个函数。这是一个很重要的功能,它是一个好的方法,能让你发现函数中存在的重复的代码,减少这样的重复。
function Cook( i1, i2, f )
{
alert("get the " + i1);
f(i1);
f(i2);
}
Cook( "lobster", "water", PutInPot );
Cook( "chicken", "coconut", BoomBoom );
看见了没!我们把一个函数当做了参数。
你的语言能这样做吗?
且慢… 如果你还没有写出PutInPot 或 BoomBoom 函数呢。如果你能把他们写成内联函数,而不是要在其它地方先声明,这样是不是更好?
Cook( "lobster",
"water",
function(x) { alert("pot " + x); } );
Cook( "chicken",
"coconut",
function(x) { alert("boom " + x); } );
老天,这太方便了。注意到了没有,我即时创建了一个方法,甚至都不用麻烦给它起名,只需掂着它的耳朵把它丢进函数里。
当你开始思考把匿名函数当作参数时,你也许会注意到有一种代码到处都是,就是,遍历数组里的所有元素进行操作。
var a = [1,2,3];
for (i=0; i
{
a[i] = a[i] * 2;
}
for (i=0; i
{
alert(a[i]);
}
对数组里的每个元素进行操作是一种很常见的动作,你可以写出一个函数,让它为你做这些:
function map(fn, a)
{
for (i = 0; i < a.length; i++)
{
a[i] = fn(a[i]);
}
}
现在,你可以把上面的代码重写成这样:
map( function(x){return x*2;}, a );
map( alert, a );
另一个常见的跟数组相关的操作是,通过某种方式把数组里的所有值组合到一起。
function sum(a)
{
var s = 0;
for (i = 0; i < a.length; i++)
s += a[i];
return s;
}
function join(a)
{
var s = "";
for (i = 0; i < a.length; i++)
s += a[i];
return s;
}
alert(sum([1,2,3]));
alert(join(["a","b","c"]));
sum 和 join 看起来非常的相似,你也许会想把它们的通用之处提取出来做成一个能把数组里的元素合并成一个值的通用函数:
function reduce(fn, a, init)
{
var s = init;
for (i = 0; i < a.length; i++)
s = fn( s, a[i] );
return s;
}
function sum(a)
{
return reduce( function(a, b){ return a + b; },
a, 0 );
}
function join(a)
{
return reduce( function(a, b){ return a + b; },
a, "" );
}
很多老式的语言根本没有方法做出这种事情。另外一些语言允许你做这些,但不容易(例如,C语言里有函数指针,但你必须进行声明,并要在什么地方定义它)。面向对象的语言并没有被证实可以允许你对函数做所有的操作。
如果你想在Java里把函数作为一个一等(First Class)对象,你需要建一个只包含一个用来调用功能点的方法的整个对象。把这种现象跟实际情况联系起来,很多的面向对象语言都会要求你为每个 class创建一个完整的文件,非常的没效率。如果你的编程语言里要求你去这样的调用功能点,那你根本没有享受到现代语言环境给你带来的所有好处。看看能否退货吧,挽回一点损失。