[TOC]
# 函数
<br>
## 概念:
  把一段相对独立的具有特定功能的代码块封装到 {} 中,形成一个独立的代码块,再给这个代码块起个名字,名字和代码块的组合就是函数,一个函数可以在后续开发中重复使用。
  总的来说,函数就是使用{}封装起来的一段代码,可供将来重复使用。
<br>
## **函数的定义**
### **语法格式**
>[success]//命名函数
function 函数名(){
// 函数体
}
function:关键字,函数、方法的意思。
函数名:遵循变量的命名规则和规范
():固定格式
{}:封装具有特定功能的代码
函数体:要实现的功能代码
实例:
定义一个函数,实现在浏览器控制台输出 '你好!' .
```
// 声明函数
function sayHello() {
console.log("你好!");
}
```
<br>
### **函数表达式**
>[success]//匿名函数
var fn = function() {
// 函数体
}
将一个匿名函数赋值给变量fn,此时可以使用fn调用该匿名函数。
实例:
定义一个匿名函数,实现在浏览器控制台输出 '你好!' .
```
var sayHello = function () {
console.log("你好!");
}
```
### **特点**
  函数声明的时候,函数体并不会执行,只有当函数被调用的时候才会执行。函数一般都用来干一件事情,故函数名需用使用动词+名词,表示做一件事情, 比如 sayHello.
<br>
## **函数的调用**
### **语法格式**
>[success]函数名();
实例:
```
// 调用函数
sayHello();
```
### **特点**
>[info] 1. 函数体只有在调用的时候才会执行,调用需要()进行调用。
> 2. 可以调用多次(重复使用)。
> 3. 函数可以在任何地方调用。
## 函数的执行过程流程
  函数调用完毕之后,程序会回到调用处,执行完调用处的代码,再往下继续执行!
函数的执行过程如下图:
![](images/函数的执行过程.png)
实例:
 求1-100之间所有数的和
 将求和的功能代码封装到函数中。
```
// 定义一个函数
function getSum() {
var sum = 0;
for (var i = 1; i <=100 ; i++) {
sum += i;
}
console.log(sum);
}
//调用函数
//格式:函数名();
getSum();
```
<br>
## **函数的参数**
### **为什么要有参数**
**需求**
求1-100之间所有整数的和
```
function getSum() {
var sum = 0;
for (var i = 1; i <=100 ; i++) {
sum += i;
}
console.log(sum);
}
```
  思考:虽然上面代码可以重复调用,但是只能计算函数体中定义好的那个范围的和,如果想要计算200至300或者其他范围的和,应该怎么办呢?
<br>
### **语法格式**
  函数内部是一个封闭的环境,可以通过传递参数的方式,把外部的值传递给函数内部。
>[success]// 带参数的函数声明
>function 函数名(形参1, 形参2, 形参...){
> // 函数体
>}
>
>[success]// 带参数的函数调用
>函数名(实参1, 实参2, 实参3);
>参1 = 实参1;
>形参2 = 实参2;
>...
<br>
### **形参和实参**
>[info]// 1、形式参数:在声明一个函数的时候,为了函数的功能更加灵活,有些值是固>定不了的,对于这些固定不了的值。我们可以给函数设置参数。这个参数没有具体的值,>仅仅起到一个占位置的作用,我们通常称之为形式参数,也叫形参。
>
>2、实际参数:如果函数在声明时,设置了形参,那么在函数调用的时候就需要传入对应的参数,我们把传入的参数叫做实际参数,也叫实参。
### 需求
1. 求两个数之和(练习)
>[info]思路:x,y 是实参,有具体的值。函数执行的时候会把x,y复制一份给函数内部的a和b,函数内部的值是复制的新值,无法修改外部的x,y
~~~
//求两个数的和
function getSum(a, b) {//括号中的a,b是形式参数
var sum = a + b;
console.log(sum);
}
//调用带有参数的函数
getSum(1, 2);//传入实际参数,相当于 a=1;b=2
//实参的另一种写法
var x = 3;
var y = 4;
getSum(x, y);
~~~
>[info]思考:如果在getSum函数内部修改a,b的值,会不会影响x,y的值?
2. 求1-n之间所有数的和(练习)
>[info]思路:跟我们之前学习的求一个数的和类似,此时需要将这个数作为函数的参数传给函数,然后在函数中做求和操作。
~~~
function getSum(n) {
//累加和
var sum = 0;
for (var i = 1; i <= n; i++) {
sum += i;
}
console.log(sum);
}
getSum(5);
~~~
3. 求n-m之间所有数的和(练习)
从n开始遍历,到m结束
~~~
function getSum(n, m) {
//累加和
var sum = 0;
for (var i = n; i <= m; i++) {
sum += i;
}
console.log(sum);
}
getSum(1, 100);
~~~
4. 求圆的面积
>[info]圆的面积公式:A = πR\*R,我们只需要将圆的半径r传给函数即可
~~~
// 圆的面积公式:A = πR*R
function getArea(r) {
var area = 3.14*r*r;
console.log(area);
}
getArea(2);
~~~
5. 定义一个函数,用于求2个数中的最大值
>[info]思路:给函数定义两个形参,用于接收外部的两个数
~~~
function getMax(n, m) {
if (n > m) {
console.log('最大值:' + n);
} else {
console.log('最大值:' + m);
}
}
getMax(11, 9);
~~~
6. 求3个数中的最大值
此时给函数定义三个形参就可以了
~~~
function getMax(a, b, c) {
if (a > b && a > c) {
console.log('最大值:' + a);
} else if (b > a && b > c) {
console.log('最大值:' + b);
} else if (c > b && c > a) {
console.log('最大值:' + c);
}
}
getMax(4, 6, 3);
~~~
7. 定义一个函数,求n个数的最大值
~~~
function getMax(array) {
//假设最大值是max
var max = array[0];
for (var i = 0; i < array.length; i++) {
//判断最大值是不是max
if (max < array[i]) {
//存储两个数中的最大值
max = array[i];
}
}
console.log(max);
}
var arr = [2, 3, 433, 5, 7, 8, 121, 2, 23, 67, 4, 12, 14];
getMax(arr);
~~~
8. 定义一个函数,判断一个数是否是素数(质数)
>[info]素数(质数):只能被1和自己整除的数
~~~
// 素数(质数):只能被1和自己整除的数
function isPrimeNum(num) {
var isPrime = true;
//遍历2-n之间的数
for(var i = 2; i< num; i++){
//判断num是否是质数
if (num % i === 0) {
//不是质数
isPrime = false;
break;
}
}
if (isPrime) {
console.log(num+'是素数');
}else {
console.log(num+'不是素数');
}
}
//调用函数
isPrimeNum(101);
~~~
<br>
## **函数的返回值**
  当函数执行完的时候,并不是所有时候都要把结果打印。我们期望函数给我一些反馈(比如计算的结果返回进行后续的运算),这个时候可以让函数返回一些东西。也就是返回值。函数通过return返回一个返回值
### **语法格式**
>[success]//定义一个带返回值的函数
function 函数名(形参1, 形参2, 形参...){
//函数体
return 值;
}
函数的返回值会返回到调用处,可以通过变量来接收这个返回值
>[success]var 变量 = 函数名(实参1, 实参2, 实参3);
函数的调用结果就是返回值,因此我们可以直接对函数调用结果进行操作。
返回值详解:
1. 如果函数中没有写 return语句 ,那么函数有默认的返回值:undefined
2. 如果函数中写了return语句,但是return后面没有任何值,那么函数的返回值也是:undefined
3. 如果函数中写了return语句,并且return后面跟着一个值,那么该值就是函数的返回值在函数的执行过程中,如果遇到return语句,那么执行完 return 语句之后,函数就返回了,即函数执行结束。
>[info]小结
  如果函数没有return语句或者return语句后面没有返回值,则函数返回undefined值,如果return后面有值,则返回该值,return除了能返回指定的值,还能结束函数。
推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。
#### 需求
1. 定义一个函数,求一组数中的最大值,将最大值返回到调用处
跟以前求最大值方式是一样的,只不过多了函数的入参(传入参数)和回参(返回值)。
~~~
function getMax(arr) {
//函数体:要执行的代码
var max = arr[0];
for (var i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
return max ;
}
var a = [3, 5, 1, 66, 8, 18];
var result = getMax(a);
console.log(result);
~~~
2. 定义一个函数,求一组数中的最小值,将最小值返回到调用处
```
// 求一组数中的最小值
function getMin(arr) {
//函数体:要执行的代码
var min = arr[0];
for (var i = 0; i < arr.length; i++) {
if (min > arr[i]) {
min = arr[i];
}
}
return min ;
}
var a = [3, 5, 1, 66, 0, -1, 8, 18];
var result = getMin(a);
console.log(result);
```
3. 定义一个函数,求整数n阶乘,,将阶乘的结果返回到调用处
4n的阶乘就是1至n的所有整数的累乘。如 5! 表示 5的阶乘,可写成 1*2*3*4*5,求得结果是 120
```
function getFactorial(num){
//函数体
var factorial = 1;
for (var i = 1; i <= num; i++) {
factorial *= i;
}
//函数返回语句
return factorial;
}
var result = getFactorial(20);
console.log(result);
```
4. 定义一个函数,求1!+2!+3!+....+n!,,将结果返回到调用处
在for循环中累加n以内所有整数的阶乘(调用 getFactorial),求得结果。
~~~
//(1)求阶乘
function getFactorial(num){
//函数体
var factorial = 1;
for (var i = 1; i <= num; i++) {
factorial *= i;
}
//函数返回语句
return factorial;
}
//(2)累加阶乘的结果
function getFactorialSum(num){
var sum = 0;
for (var i = 1; i <= num; i++) {
sum = sum + getFactorial(i);//将阶乘累加
}
return sum ;
}
var resutl = getFactorialSum(3);
console.log(resutl);
~~~
<br>
### **arguments的使用**
JavaScript中,**arguments是一个类似数组的对象,arguments对象作为函数的一个内置属性,所有函数都内置了一个arguments对象,arguments对象中存储了外部传递给函数的所有的实参。arguments是一个伪数组(具有数据的部分属性),因此其可以进行遍历。**
通过arguments获取外部传给函数的参数
~~~
function test1() {
//通过arguments获取函数调用时传入的实参
console.log(arguments);
console.log(123);
}
test1(5, 6);
~~~
**需求**
1. 使用arguments属性,求任意个数的最大值
思路:通过在函数内部使用arguments,可以获取到外出传给函数的所有是实参
~~~
function getMax() {
//获取arguments中的第一个元素
var max = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (max < arguments[i]) {
max = arguments[i];
}
}
return max;
}
var max = getMax(3, 5, 2, 11, 8, 1, 0);
console.log(max);
~~~
2. 求任意个数的和
思路:此题要求熟练使用arguments的索引
~~~
function getSum() {
return arguments[0] + arguments[1];
}
console.log(getSum(4, 7));
~~~
### **实例**
1. 定义一个函数,求斐波那契数列 1 1 2 3 5 8 13 21... 中的第n个数的值,并将该值返回通过观察可以知道斐波那契数列( Fibonacci )的规律:第一项+第二项 = 第三项,即前两项之和等于第三项。假设第三项是n3,则它的前一项就是n2,前两项就是n1,已知n1和n2,则n3可以由n1+n2求得,所以不管n是多少,都有n1+n2=n3;n3+n4=n5 ...... (n-2) + (n-1) = n 推导得出。
~~~
//用于记录n的前两项
var n1 = 1;
//用于记录n的前一项
var n2 = 1;
//用于记录n
var n3;
function getFib(n) {
//因为n1和n2已经确定是1,所以循环从第三项开始
for (var i = 3; i <= n; i++) {
n3 = n1 + n2;
//每求得一项,就要向前递推数据
n1 = n2;
n2 = n3;
}
return n3;
}
console.log(getFib(8));
~~~
2. 定义一个函数,用于翻转数组,返回翻转后的新数组
注意翻转数组的关键在于:`newArray[newArray.length] = array[i];` 逆向遍历数组和依次存入新数组。
~~~
function reverse(array) {
var newArray = [];
for (var i = array.length - 1; i >= 0; i--) {
newArray[newArray.length] = array[i];
}
return newArray;
}
// var arr = [1, 2, 3, 0, 9, 7, 12];
var str = ['ac', 'yy', 'zs'];
console.log(reverse(str));
~~~
3. 定义一个函数,用于对数组作从小到大排序
>[success]思路:此题可以使用我们之前学过的冒泡排序算法
~~~
//定义一个数组
var arr = [86, 57, 99, 61, 18, 40, 34];
function sort(array) {
//外层循环:控制比较的趟数
for (var i = 0; i < array.length - 1; i++) {
//假设已经排序完成
var isOver = true;
//内层循环:控制比较的次数,比较元素大小,交换元素
for (var j = 0; j < array.length - 1 - i; j++) {
//比较两个元素的大小
if (array[j] > array[j + 1]) {
//未完成排序
isOver = false;
//交换元素位置
var temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
//如果已经排序完成,则终止循环
if(isOver){
break;
}
}
//返回排好的数组
return array;
}
//调用函数
var a = sort(arr);
console.log(a);
~~~
4. 定义一个函数,函数可以接收一个年份,函数中判断该年份是否是闰年
>[success]什么是闰年?满足以下两点中的任何一点的年份,就是闰年!
(1)能被4整数并且不能被100整数
(2)能被400整数
```
function isLeapYear(year) {
var isRun;
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
//闰年
isRun = true;
} else {
//平年
isRun = false;
}
return isRun;
}
var result = isLeapYear(1988);
//将结果转成文字
var isRun = result?'闰年':'平年';
console.log(isRun);
```
5. 定义一个函数,输入年月日,判断这一天是这一年的第几天?
>[info]需求分析:
(1) 循环累加当前月份的天数, 1,3,5,7,8,10,12月,每月31天, 4,6,10,11月,每个月30天;
(2) 2月份需要判断闰年,调用前面写好的isLeapYear()函数即可;
~~~
//定义函数
function getDay(year, month, day) {
//接收外部变量
var d = day;
//循环每个月,累加每个月的天数
for (var i = 1; i < month; i++) {
//使用if语句
if (i === 1 || i === 3 || i === 5 || i === 7 || i === 8 || i === 10 || i === 12) {
d += 31;
} else if (i === 4 || i === 6 || i === 9 || i === 11) {
d += 30;
} else {
// 2月份
//调用判断闰年的方法
if (isLeapYear(year)) {
d += 29;
console.log('闰年');
} else {
d += 28;
console.log('平年');
}
}
}
return d;
}
// 测试
var d1 = getDay(1988, 3, 2);//60+2
console.log(d1);
var d2 = getDay(1989, 4, 2);//90+2
console.log(d2);
~~~
<br>
## **匿名函数**
  没有名字的函数称作匿名函数。如下这种定义函数的方式叫函数表达式,函数表达式等号右边部分的函数没有名字,叫匿名函数。
>[success]var fn = function() { // 函数体}
### **语法格式**
>[success]//定义
var 变量名 = function( [形参1,形参2...] ){
  //函数体
  [return 返回值;]
}
//调用
var 返回值 = 变量名();
举例
```
//1.定义无参数、返回值的匿名函数
//定义匿名函,并将匿名函数赋值给一个变量,这样就可以通过变量进行调用
var fn = function() {
// 函数体
console.log('hello');
}
//2.调用匿名函数
fn();
```
```
//1.定义有参数、返回值匿名函数
//定义匿名函,并将匿名函数赋值给一个变量,这样就可以通过变量进行调用
var fn = function(a, b) {
// 函数体
console.log('hello');
return a + b;
}
//2.调用匿名函数
var c = fn(2, 3);
```
## **自调用函数(了解)**
  匿名函数不能通过直接调用来执行,所以需要将其赋值给一个变量,才能完成调用。除了赋值给变量的方式外,还可以通过自调用的方式来调用(执行)匿名函数。
>[success]格式:()();
解释:第一个小括号写的是匿名函数的定义,第二个小括号写的是需要给匿名函数传入的参数,如下:
(function (a) {
alert(a);
})(888);
//以上代码输出结果是 888
## 函数是一种数据类型(熟悉)
  函数是一种数据类型,所以我们可以使用typeof关键字查看函数的类型。
~~~
function fn() {}
console.log(typeof fn);//function
~~~
### **函数作为参数**
  因为函数也是一种数据类型,所以可以把一个函数当作另一个函数的实参传入,然后在另一个函数中调用。
>[success]//定义函数,用于当做另一个函数的实参
function fn() {
console.log('我是fn函数');
}
//定义函数,形参用于接收其他函数
function test(func){
func();//调用外部传入的函数
}
//调用test函数,将其他函数作为实参传入
test(fn);
### **函数作为返回值**
  因为函数也是一种数据类型,所以可以把一个函数当作另一个函数的返回值,从另一个函数内部返回。
//写法一
```
function fn() {
//定义一个匿名函数 test
var test = function () {
alert(1+2);
}
//返回一个函数
return test;
}
```
//写法二
```
function fn() {
//返回一个函数
return function () {
alert(1+2);
}
}
```
>[info] //使用变量来接收fn函数的返回值,返回值是一个函数
> var func = fn();
> //调用返回的函数
> func();
> //以上步骤可以简写为一步
> fn()();
> //解释:以上fn()();的执行可分为两个步骤
> //步骤一:fn(),返回一个函数,此时可认为系统为返回的函数起了一个随机名字;
> //步骤二:(); 其实是 随机名字(); 完成返回的函数的调用。
<br>
### 代码规范(记住)
  规范的代码整齐美观,易于阅读和维护,能加快开发人员对代码的理解。以下从命名规范、变量规范、注释规范、空格规范、换行规范等角度来学习代码规范。
>[success] 1.命名规范
> 变量、函数 的命名 必须要有意义
> 变量 的名称一般用名词
> 函数 的名称一般用动词或 动词+名词
> 2.变量规范
> 操作符的前后要有空格
> var name = 'zs'; 5 + 6
> 3.注释规范
> // 这里是注释
> 4.空格规范
> if (true) {
>
> }
> for (var i = 0; i <= 100; i++) {
>
> }
> 5.换行规范
> var arr = [1, 2, 3, 4];
> if (a > b) {
>
> }
> for (var i = 0; i < 10; i++) {
>
> }
> function fn() {
>
> }
WebStorm工具生成的代码遵循规范,我们可以利用工具的这个特点。格式化快捷键:**Ctrl + alt + L**