0%

函数表达式一之递归

函数表达式

定义函数有两种方式:函数声明和函数表达式。

函数声明

Firefox、Safari、Chrome、Opera支持函数的非标准属性name,这个属性的值总等于function关键字后面的标识符。

函数声明的一个重要特征就是函数声明提升

1
2
3
4
sayHi();  //没毛病,妥妥的
function sayHi(){
console.log('Hi!');
}

函数表达式

函数表达式的最常用形式:

1
2
3
var functionName = function(){
//do something
}

函数表达式在使用时必须先赋值,下面代码就会导致错误:

1
2
3
4
sayHi();  //报错
var sayHi = function(){
console.log('Hi!');
}

这样是不行滴

1
2
3
4
5
6
7
8
9
10
if(condition){
function sayHi(){
console.log('Hi!');
}
}
else{
function sayHi(){
console.log('Yo!');
}
}

以上代码并不会像我们想象的那样根据condition的值来返回不同的函数声明,而是在不同的浏览器之间有着不同的实现。大多数浏览器会返回第二个声明,而忽略condition;Firefox会在condition为true时返回第一个声明。由于表现的不一致,最好不要这么干(小伙子你在玩火你知道吗?)。

改成函数表达式就行了

1
2
3
4
5
6
7
8
9
10
11
var sayHi
if(condition){
sayHi = function (){
console.log('Hi!');
};
}
else{
sayHi = function (){
console.log('Yo!');
};
}

作为函数返回值的匿名函数

既然函数可以作为值赋给变量,也就可以作为函数的返回值而存在,就像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function createComparisonFunction(propertyName){
return function(obj1,obj2){
var value1 = obj1[propertyName];
var value2 = obj2[propertyName];

if(value1 < value2){
return -1;
}
else if(value1 > value2){
return 1;
}
else{
return 0;
}
};
}
var data = [{name: "Zachary", age: 28}, {name: "Nicholas", age: 29}];

data.sort(createComparisonFunction('name'));
console.log(data[0].name); //Nicholas

data.sort(createComparisonFunction('age'));
console.log(data[0].name); //Zachary

递归

经典递归

递归函数是在一个函数通过名字调用自身的情况下构成的,来看下面这个经典的递归函数:

1
2
3
4
5
6
7
8
9
function factorial(num){
if(num <= 1){
return 1;
}
else{
return num * factorial(num-1);
}
}
factorial(5); //120

解耦和

上面的例子是跟函数名挂钩的,如果函数名被清空了,就会出现问题:

1
2
3
var factorial2 = factorial;
factorial = null;
factorial2(5); //error:factorial is not defined

可以使用argument.callee来解除这种耦合,因为arguments.callee指向拥有这个arguments对象的函数:

1
2
3
4
5
6
7
8
9
10
11
12
function factorial(num){
if(num <= 1){
return 1;
}
else{
return num * arguments.callee(num-1);
}
}

var factorial2 = factorial;
factorial = null;
factorial2(5); //120

严格模式下的递归

由于arguments.callee在严格模式下访问会导致出错,所以严格模式下,就得换一种思路:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var factorial = (function f(num){
if(num <= 1){
return 1;
}
else{
return num * f(num-1);
}
});

var factorial2 = factorial;
factorial = null;
factorial2(5); //120

//f = null; //error:f is not defined

上面的示例代码把一个名为f()的命名函数表达式赋值给了变量factorial,之后即使把这个函数赋值给另一个变量,函数名f仍然有效。可以看到代码中最后一行无法对f进行赋值操作。因为与函数声明不同的是,函数表达式中的命名函数的函数名并不是一个变量。关于这一点,可以看下面这个例子:

1
2
3
4
5
6
7
8
9
10
function fn1(){
console.log('fn1');
}

var fn = function fn2(){
console.log('fn2');
}

console.log(fn1); //打印出第一个函数声明的所有代码,包括空格
console.log(fn2); //error:fn2 is not defined

喵了个喵~