0%

函数

这章呢学习的概念性的东西会比较多,但是逻辑的东西并不多。

5.1 函数定义

函数的定义是什么,简单点来说就是起到分装的作用。何为分装?假设我们家里没有衣柜、鞋柜等东西,所有的东西都是堆在一起的,包括衣服、鞋子、被子啊等等,是不是就会显得杂乱无章,这就需要一个分装。什么是分装呢,就是拿一个盒子,把东西分类包装好,下次需要用的时候,直接到盒子里面寻找,这样目的性就比较强了,这就是分装的性质。那么函数分装是什么,函数分装的是过程,也就是说把一个操作过程分装起来,这样就比较好找、好用,这是第一步;第二步呢,函数也可以反过去调用。

5.11 函数声明

函数声明,是一种独立的结构,它会声明一个具名函数,并必须以function开头。且函数声明会进行函数提升,使它能在其所在作用域的任意位置被调用,即后面的代码可以将此函数通过函数名赋值给变量或者对象属性。结构如下:

1
2
3
4
5
6
/*函数的声明
function <函数名>([<形参,可有一个或多个>]){
函数体
}
函数可以有形参,也可以没有;可以有返回,也可以没有,没有的话,返回 undefined
*/

例如,以下的代码定义了一个函数名为 reverseNumber 的函数:

1
2
3
4
5
6
7
8
9
10
11
12
//12345返回变成 54321  ,用函数实现
function reverseNumber(num){
if(typeof num != 'number'){
return '';
}
if(!isFinite(num) || isNaN(num)){
return '';
}
return +num.toString().split('').reverse().join('');

}
console.log(reverseNumber(12345));//54321

5.12 函数表达式(命名、匿名)

虽然上面的函数声明在语法上是一个语句,但函数也可以由函数表达式创建。

1
2
3
4
//函数表达式  
(function f(){})
var f=function ff(){};
ff(a,fuction fff(){},b);

函数声明和函数表达式最大的区别是:函数声明的函数名在系统里面可以找到名字,但是,函数表达式不可以。以下代码定义了一个 fact 函数:

1
2
3
4
5
6
7
8
var f = function fact(x){
if(x<=1){
return 1;
}else{
return x*fact(x-1);//阶乘 5!=5*4*3*2*1
}
};
console.log(f(5));//120

我们在浏览器的系统中查找 fact 这个函数名,可以看到,

所以这也是函数声明和函数表达式的最大区别,那么,在这边,我们也可以这样写,两者实现的效果是一样的,

1
2
3
4
5
6
7
8
9
var f = function(x){
if(x<=1){
return 1;
}else{
return x*arguments.callee(x-1);//阶乘 5!=5*4*3*2*1
}
};
console.log(f(5));

这个写法大家目前先记住,简而言之,arguments.callee 就是代替表达式的函数名。函数表达式这块呢,大家一定要去理解,因为我们后边学的基本上都是用的函数表达式。

5.13 函数作为参数传递

函数呢,也可以被当作接力棒,作为参数传递给另外一个函数,实例说明下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//函数可以作为参数传给另外一个函数
function f1(f){//f 是形参
console.log(f);
console.log(f());
}
//f2()是函数声明
function f2(){
console.log('I am f2()');
return 'f2 say hello!';
}
f1(f2);

//f3 是函数表达式
var f3=function(){
console.log('I am f3()');
return 'f3 say hello!';
};
f1(f3);

其实很简单,就是在外面包一层另外的函数,然后在这个另外函数里面执行真正的函数。另外, function 是一个引用类型的对象,看例子:

1
2
3
4
5
6
console.log((f3=function(x){
console.log('I am f3()');
return 'f3 say hello to ' + x +'!';
})('wanglin'));
//I am f3()
// f3 say hello to wanglin!

底下我们来看两个值互相交换,要怎么写呢?详解请看下例,

1
2
3
4
5
6
7
8
9
var x=5;
var y=6;
function exchange(a,b){
var c=a;
a=b;
b=c;
}
exchange(x,y);
console.log(x + ' ' + y);//5 6

诶?怎么回事,没有发生交换啊,别慌,再来,

1
2
3
4
5
6
7
8
9
var x=[5];
var y=[6];
function exchange(a,b){
var c=a[0];
a[0]=b[0];
b[0]=c;
}
exchange(x,y);
console.log(x + ' ' + y);//6 5

为什么 x,y 的类型换成数组,就可以交换数值了呢?这就是我们为什么要试图去理解引用和值,值在函数里边是换不过来的,但是,如果是引用的话是可以的,因为引用操作的是真实的堆里面的东西。

5.14 嵌套函数

在 JavaScript 中,函数可以嵌套在另一个函数中进行定义,有点类似 Java 中的内部类,

1
2
3
4
5
6
7
8
9
10
function hypotenuse(a,b){
//function square(x){
// return x*x;
//}
var square=function(x){
return x*x;
}
return Math.sqrt(square(a)+square(b));
}
console.log(hypotenuse(3,4));//5

在上面的代码片段中,函数 hypotenuse 中嵌套定义了函数 square 。这时,函数square只能在distance中被调用。如果我们想要在distance之外调用square函数,由于JavaScript中函数执行环境和变量作用域的限制,我们将得到一个很不友好的错误提示!所以,嵌套函数在未来我们讲闭包的时候带来很多很多的问题。

5.2 函数的调用

5.21 函数调用

JavaScript 中函数有4种调用方式,每种方式的不同在于 this 的初始化。一般而言,在 JavaScript 中, this 指向函数执行的当前对象。

作为一个函数调用,

1
2
3
4
function myFunction(a, b) {
return a * b;
}
myFunction(10, 2); // myFunction(10, 2) 返回 20

作为方法调用,

1
2
3
4
5
var obj={};
obj.add=function(a,b){
return a+b;
};
console.log(obj.add(1,2));//3

作为构造函数调用,构造函数调用这个的话,后面章节会继续深入讲解,

1
2
// 3.构造函数
var arr=new Array();

间接调用 间接调用和直接调用唯一的区别是可以绑定 this 指针。如果不考虑 this ,下面这三种写法完全一样的。

1
2
3
4
5
6
function hypotenuse(a,b){
return Math.sqrt(a*a+b*b);
}
this.hypotenuse(3,4);
hypotenuse.call(this,3,4);
hypotenuse.apply(this,[3,4]);

5.22 形参和实参

什么是形参,什么又是实参?形参和实参是不一样的,数量、类型都可以不一样,形参和实参会绑定,动态关联。

1
2
3
4
5
6
7
8
9
10
function foo(a,b,c){
console.log(foo.length);
console.log(arguments)

arguments[1]=-1;
console.log(b);
}
foo(1);
foo(1,2,3,4,5,6);

5.3 递归

递归构造:

  • 找到已知 f(x-1) 情况下,如何求解 f(x) 的通式;
  • 找到递归的基石,比如 f(0) 、f(1) 的值;

递归真正在工业中是很少使用的,一般是适用于学校性的学习。但是,递归的用法还是挺好用的,主要是考虑到栈的问题,当然,如果系统分配的栈大一点的话,整个递归问题就会进一步得到改善。我们来看个递归的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//递归的例子
//有一行台阶,阶数是 n 。一次走一步,两步,三步,对于阶数为 n 的一共有多少种走法?

function step(n){
switch(n){
case 1:
return 1;
break;
case 2:
return 2;
break;
case 3:
return 4;
break;
default:
return (step(n-3)+step(n-2)+step(n-1));
break;

}
return '';
}
console.log(step(5));//13
//step(1):1
//step(2):2
//step(3):1+step(2)+step(1)=4
//step(4):step(1)+step(2)+step(3)=7
//step(n)=step(n-3)+step(n-2)+step(n-1)

您的支持是对我最大的鼓励