## 函数 function

函数:就是将一些语句(功能)进行**封装**,然后通过**调用**的形式,执行这些语句。

- **函数也是一个对象**

- 使用`typeof`检查一个函数对象时,会返回function

**函数的作用:**

- 将大量重复的语句写在函数里,以后需要这些语句的时候,可以直接调用函数,避免重复劳动。

- 简化编程,让编程模块化。

先来看个例子:

```javascript
	console.log("你好");
	sayHello();		//调用函数
	//定义函数:
	function sayHello(){
		console.log("欢迎");
		console.log("welcome");
	}
```


我们在实际开发中很少使用构造函数来创建一个函数对象。



### 第一步:函数的定义

**方式一**:使用`函数声明`来创建一个函数。语法:


```javascript
	function 函数名([形参1,形参2...形参N]){
		语句...
	}
```

举例:

```javascript
	function sum(a, b){
		return a+b;
	}
```

解释如下:

- function:是一个关键字。中文是“函数”、“功能”。

- 函数名字:命名规定和变量的命名规定一样。只能是字母、数字、下划线、美元符号,不能以数字开头。

- 参数:后面有一对小括号,里面是放参数用的。

- 大括号里面,是这个函数的语句。

PS:方法写完之后,我们在方法的前面输入`/**`,然后回车,会发现,注释的格式会自动补齐。


**方式二**:使用`函数表达式`来创建一个函数。语法:


```javascript
	var 函数名  = function([形参1,形参2...形参N]){
		语句....
	}
```


举例:

```javascript
	var fun3 = function() {
		console.log("我是匿名函数中封装的代码");
	};
```


### 第二步:函数的调用

函数调用的语法:

```javascript
	函数名字();
```

## 函数的参数:形参和实参

函数的参数包括形参和实参。来看下面的图就懂了:


![](http://img.smyhvae.com/20180118_1130.png)

假设我们定义一个求和的函数。

**形参:**

- 可以在函数的`()`中来指定一个或多个形参。

- 多个形参之间使用`,`隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值。


**实参**:

- 在调用函数时,可以在()中指定实参。

- 实参将会赋值给函数中对应的形参。

举例:

```javascript
		sum(3,4);
		sum("3",4);
		sum("Hello","World");

		//函数:求和
		function sum(a, b) {
			console.log(a + b);
		}
```

控制台输出结果:

```
	7
	34
	helloworld
```

**实参的类型:**


函数的实参可以是任意的数据类型。

调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查。



**实参的数量:**

调用函数时,解析器也不会检查实参的数量:

- 多余实参不会被赋值

- 如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined。例如:

20180314_2030.png

### 函数的返回值

举例:

```javascript
		console.log(sum(3, 4));
		//函数:求和
		function sum(a, b) {
			return a + b;
		}
```

return的作用是结束方法。

注意:

- return后的值将会会作为函数的执行结果返回,可以定义一个变量,来接收该结果。

- 在函数中return后的语句都不会执行(函数在执行完 return 语句之后停止并立即退出)

- 如果return语句后不跟任何值,就相当于返回一个undefined

- 如果函数中不写return,则也会返回undefined

- 返回值可以是任意的数据类型,可以是对象,也可以是函数。


### 函数名、函数体和函数加载问题(重要,请记住)

我们要记住:**函数名 == 整个函数**。举例:

```javascript
        console.log(fn) == console.log(function fn(){alert(1)});

        //定义fn方法
        function fn(){
            alert(1)
        };
```

我们知道,当我们在调用一个函数时,通常使用`函数()`这种格式;但此时,我们是直接使用`函数`这种格式,它的作用相当于整个函数。

**函数的加载问题**:JS加载的时候,只加载函数名,不加载函数体。所以如果想使用内部的成员变量,需要调用函数。


### 回调函数

我们知道,当函数自己调用自己时,称之为递归。本段来讲一下回调。

回调:**函数作为参数**,进行传递和使用,称之为**回调**。

举个最简单的例子:

```javascript
        fn(test);   //回调test这个函数

        function test() {
            console.log("我是被回调的函数")
        }

```

输出结果:我是被回调的函数。


**什么情况下使用回调函数:**

回调函数一般是用于**定义一个规则**。因为我们无法通过传递一个变量来传递规则。

换而言之:当我们需要**传递规则的时候,必须使用回调**。规则的传递只能通过函数来实现,无法通过变量实现。


举例:

```javascript
        console.log(fn(12,3,test1));    //注意第三个参数是函数名
        console.log(fn(12,3,test2));    //注意第三个参数是函数名

        //定义一个通用规则(包含加减乘数的抽象函数)
        fn(num1,num2,demo) {
            return demo(num1,num2);
        }

        //定义四个规则:加减乘除
        function test1(a,b) {
            return a+b;
        }
        function test2(a,b) {
            return a-b;
        }
        function test3(a,b) {
            return a*b;
        }
        function test4(a,b) {
            return a/b;
        }
```


打印结果:

```
	15
	9
```


## fn()  和 fn 的区别【重要】

- `fn()`:调用函数。相当于获取了函数的返回值。

- `fn`:函数对象。相当于直接获取了函数对象。


## 立即执行函数

匿名函数如下:

```javascript
	function(a, b) {
		console.log("a = " + a);
		console.log("b = " + b);
	};
	```


立即执行函数如下:

```javascript
		(function(a, b) {
			console.log("a = " + a);
			console.log("b = " + b);
		})(123, 456);
```


立即执行函数:函数定义完,立即被调用,这种函数叫做立即执行函数。

立即执行函数往往只会执行一次。


## arguments

在调用函数时,浏览器每次都会传递进两个隐含的参数:

- 1.函数的上下文对象 this

- 2.**封装实参的对象** arguments

例如:

```javascript
        function foo() {
            console.log(arguments);
            console.log(typeof arguments);
        }

        foo();
```


20180315_0903.png

arguments是一个类数组对象,它也可以通过索引来操作数据,也可以获取长度。

在调用函数时,我们所传递的实参都会在arguments中保存。

arguments.length可以用来获取**实参的长度**。

我们即使不定义形参,也可以通过arguments来使用实参(只不过比较麻烦):arguments[0] 表示第一个实参、arguments[1] 表示第二个实参...


arguments里边有一个属性叫做callee,这个属性对应一个函数对象,就是当前正在指向的函数的对象。

```javascript
		function fun() {

			console.log(arguments.callee == fun); //打印结果为true
		}

		fun("hello");
```