之前说的三种函数声明中(参见JavaScript快速入门(二)——JavaScript变量),使用Function构造函数的声明方法比较少见,我们暂时不提。function func() { }和var func = function() { }除了在声明提升中有所不同之外也没有其他不同,我们合并起来一起看。我们在这里着重讲一个东西——匿名函数。
匿名函数顾名思义,就是没有名字的函数。它的形式就是function() { }。请注意和之前说的两种方式的区别,这里并没有赋值给任何变量,也就是说,没有指向这个函数的引用,我们无法在其他地方调用这个函数,也就是说,这种函数的使用价值只有一次。当我们把匿名函数赋值给其他变量时,就变成了var func = function() { }。是不是很熟悉?没错,就是我们之前说的变量声明的方法。而如果func是隐式声明的话,那么,这个函数就变成了全局函数。
匿名函数使用非常广泛,它常用于只执行一次的函数,例如排序函数,我们可以这样来写:
var a = [2,1,4,7,5]; a.sort(function(num1, num2) { return num1 > num2; })
我们传了个匿名函数作为参数,因为这个函数只适用于这个地方,而无法用在其他地方。但如果是类似的地方也用了类似的函数,例如我们需要两次排序:
var a = [2,1,4,7,5], b = [4,2,6,4,1]; function sortDesc(num1, num2) { return num1 > num2; } a.sort(sortDesc); b.sort(sortDesc);
我们就可以把这个匿名函数剥离出来赋给一个function类型的变量,达到重复利用的目的。
由上面这个例子我们可以看出匿名函数的优劣。坏处很明显,就是无法再次利用;好处是减少了声明的消耗(当然,如果有两次以上的利用的话,当然是声明的消耗更少)。
function add(num1, num2) { return num1 + num2; } var a = add(1, 2); // 3这种方式就是函数名(参数列表)的形式,我们在C中经常见到,就不详细说了,我们可以把上面那个换种形式来展现:
var a = (function add(num1, num2) { return num1 + num2; })(1, 2); console.log(a); // 3
var a = (function add(num1, num2) { return num1 + num2; })(1, 2); var b = add(1, 2); // error,add is not defined console.log(a); // 3 console.log(b); // undefined这是因为add这个变量的作用域仅限于括号内,这个在之后的作用域讲解中将讲到。
function getAbs(num) { function isNegative(num) { return num < 0; } return isNegative(num) ? -num : num; } var a = getAbs(-1); // 1
function add(num1, num2) { console.log(arguments); // [1, 2] return num1 + num2; }; var b = add(1, 2);注意,arguments对象保存的是实参。接下来,我们要展示JavaScript中非常有意思的一个东西,也是JavaScript灵活性的一大体现。在这之前,我们先来谈下C中的函数重载。
function add(num1, num2) { console.log(arguments); // [1, 2, 3] return num1 + num2 + arguments[2]; }; var b = add(1, 2, 3); // 6我们可以巧妙的利用arguments对象,来达到我们的目的。我们甚至可以对上面的做个扩展,让它能把所有参数的和返回,即使形参列表为空。
function add() { var sum = 0; for(var count = 0, length = arguments.length; count < length; count++) { sum += arguments[count]; } return sum; }; var b = add(1, 2, 3, 4); // 10那如果相反,形参列表长度比实参列表长呢?
function add(num1, num2) { console.log(num1); // 1 console.log(num2); // undefined return num1 + num2; }; var b = add(1); // NaN我们可以看到,超出实参长度的形参部分,就会是undefined,从而返回我们并不想要的结果(NaN表示应该是个number类型结果却是其他类型)。我们可以稍作修改:
function add(num1, num2) { num2 = num2 || 0; return num1 + num2; }; var b = add(1); // 1利用逻辑操作符的特性来将形参实例化,保证使用时形参不为undefined。当然,这样也有个别问题,如果传入的实参逻辑值也是false(例如0、undefined、null)等等,我们就需要用全等符号进行判断了,在此例中不做要求。
function getAbs(num) { function isNegative(num) { console.log(isNegative.caller); // getAbs console.log(arguments.callee); // isNegative return num < 0; } return isNegative(num) ? -num : num; } var a = getAbs(-1);你可以将这段代码运行一下,会发现,arguments.callee永远指向函数本身,而函数名.caller将指向调用该函数的代码所在函数,例如本例中即为getAbs。不过如果通过函数名.caller来寻找的话,耦合度太高。我们可以把两个结合起来,
function getAbs(num) { function isNegative(num) { console.log(arguments.callee) console.log(arguments.callee.caller) return num < 0; } return isNegative(num) ? -num : num; } var a = getAbs(-1);有人问这个有什么用?这个严格的来说不是太有用,而且其安全性有问题,否则严格模式也不会禁用掉这两个对象了。但说没用也是不可能的,要不然也不会出现这两个东西了。
function fib(num) { if(num == 1 || num == 2) { return 1; } return fib(num - 1) + fib(num - 2); } var b = fib(6); // 8但是这样的坏处在于我们如果要更改个函数名,我们将同时修改三个地方(调用的暂时不论)。我们可以用我们刚学到的东西来解决这个问题:
function fib(num) { if(num == 1 || num == 2) { return 1; } return arguments.callee(num - 1) + arguments.callee(num - 2); } var b = fib(6); // 8但是,投机取巧也是有其弊端的,这会让别人在看你的代码的时候很费劲。用不用,取决于具体情况。
JavaScript快速入门(四)——JavaScript函数
原文:http://www.cnblogs.com/smght/p/4369549.html