[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. 函數可以在任何地方調用。
## 函數的執行過程流程
  函數調用完畢之后,程序會回到調用處,執行完調用處的代碼,再往下繼續執行!
函數的執行過程如下圖:

實例:
 求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**