前端學習(33)~js學習(十):函數
- 2020 年 3 月 18 日
- 筆記
關於函數的核心內容:
- 函數有哪幾種定義和調用方式
- this:函數內部的 this 指向、如何改變 this 的指向。
- 函數的嚴格模式
- 高階函數:函數作為參數傳遞、函數作為返回值傳遞
- 閉包:閉包的作用
- 遞歸:遞歸的兩個條件
- 深拷貝和淺拷貝的區別
函數的介紹
函數:就是將一些功能或語句進行封裝,在需要的時候,通過調用的形式,執行這些語句。
- 函數也是一個對象
- 使用
typeof
檢查一個函數對象時,會返回function
函數的作用:
- 將大量重複的語句抽取出來,寫在函數里,以後需要這些語句的時候,可以直接調用函數,避免重複勞動。
- 簡化編程,讓編程模組化。高內聚、低耦合。
來看個例子:
console.log("你好"); sayHello(); // 調用函數 // 定義函數 function sayHello(){ console.log("歡迎"); console.log("welcome"); }
函數的定義/聲明
方式一:利用函數關鍵字自定義函數(命名函數)
使用函數聲明
來創建一個函數(也就是 function 關鍵字)。語法:
function 函數名([形參1,形參2...形參N]){ // 備註:語法中的中括弧,表示「可選」 語句... }
舉例:
function fun1(a, b){ return a+b; }
解釋如下:
- function:是一個關鍵字。中文是「函數」、「功能」。
- 函數名字:命名規定和變數的命名規定一樣。只能是字母、數字、下劃線、美元符號,不能以數字開頭。
- 參數:可選。
- 大括弧裡面,是這個函數的語句。
方式二:函數表達式(匿名函數)
使用函數表達式
來創建一個函數。語法:
var 變數名 = function([形參1,形參2...形參N]){ 語句.... }
舉例:
var fun2 = function() { console.log("我是匿名函數中封裝的程式碼"); };
解釋如下:
- 上面的 fun2 是變數名,不是函數名。
- 函數表達式的聲明方式跟聲明變數類似,只不過變數裡面存的是值,而函數表達式裡面存的是函數。
- 函數表達式也可以傳遞參數。
從方式二的舉例中可以看出:所謂的「函數表達式」,其實就是將匿名函數賦值給一個變數。
方式三:使用構造函數 new Function()
使用構造函數new Function()
來創建一個對象。這種方式,用的少。
語法:
var 變數名/函數名 = new Function('形參1', '形參2', '函數體');
注意,Function 裡面的參數都必須是字元串格式。也就是說,形參也必須放在字元串里;函數體也是放在字元串里包裹起來,放在 Function 的最後一個參數的位置。
程式碼舉例:
var fun3 = new Function('a', 'b', 'console.log("我是函數內部的內容"); console.log(a + b);'); fun3(1, 2); // 調用函數
列印結果:
我是函數內部的內容 3
分析:
方式3的寫法很少用,原因如下:
- 不方便書寫:寫法過於啰嗦和麻煩。
- 執行效率較低:首先需要把字元串轉換為 js 程式碼,然後再執行。
總結
1、所有的函數,都是 Fuction
的「實例」(或者說是「實例對象」)。函數本質上都是通過 new Function 得到的。
2、函數既然是實例對象,那麼,函數也屬於「對象」。還可以通過如下特徵,來佐證函數屬於對象:
(1)我們直接列印某一個函數,比如 console.log(fun2)
,發現它的裡面有__proto__
。(這個是屬於原型的知識,後續再講)
(2)我們還可以列印 console.log(fun2 instanceof Object)
,發現列印結果為 true
。這說明 fun2 函數就是屬於 Object。
函數的調用
方式1:普通函數的調用
函數調用的語法:
函數名();
或者:
函數名.call();
程式碼舉例:
function fn1() { console.log('我是函數體裡面的內容1'); } function fn2() { console.log('我是函數體裡面的內容2'); } fn1(); // 調用函數 fn2.call(); // 調用函數
方式2:通過對象的方法來調用
var obj = { a: 'qianguyihao', fn2: function() { console.log('千古壹號,永不止步!'); }, }; obj.fn2(); // 調用函數
如果一個函數是作為一個對象的屬性保存,那麼,我們稱這個函數是這個對象的方法。
方式3:立即執行函數
程式碼舉例:
(function() { console.log('我是立即執行函數'); })();
立即執行函數在定義後,會自動調用。
上面講到的這三種方式,是用得最多的。接下來講到的三種方式,暫時看不懂也沒關係,可以等學完其他的知識點,再回過頭來看。
方式4:通過構造函數來調用
程式碼舉例:
function Fun3() { console.log('千古壹號,永不止步~'); } new Fun3();
這種方式用得不多。
方式5:綁定事件函數
程式碼舉例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <div id="btn">我是按鈕,請點擊我</div> <script> var btn = document.getElementById('btn'); //2.綁定事件 btn.onclick = function() { console.log('點擊按鈕後,要做的事情'); }; </script> </body> </html>
方式6:定時器函數
程式碼舉例:(每間隔一秒,將 數字 加1)
let num = 1; setInterval(function () { num ++; console.log(num); }, 1000);
函數的參數:形參和實參
函數的參數包括形參和實參。先來看下面的圖就很好懂了:

形參:
- 概念:形式上的參數。定義函數時傳遞的參數,當時並不知道是什麼值。
- 定義函數時,可以在函數的
()
中來指定一個或多個形參。 - 多個形參之間使用
,
隔開,聲明形參就相當於在函數內部聲明了對應的變數,但是並不賦值。
實參:
- 概念:實際上的參數。調用函數時傳遞的參數,實參將會傳遞給函數中對應的形參。
- 在調用函數時,可以在函數的
()
中指定實參。
注意:實際參數和形式參數的個數,一般要相同。
舉例:
// 調用函數 sum(3,4); sum("3",4); sum("Hello","World"); // 定義函數:求和 function sum(a, b) { console.log(a + b); }
控制台輸出結果:
7 34 helloworld
實參的類型
函數的實參可以是任意的數據類型。
調用函數時,解析器不會檢查實參的類型,所以要注意,是否有可能會接收到非法的參數,如果有可能則需要對參數進行類型的檢查。
實參的數量(實參和形參的個數不匹配時)
調用函數時,解析器也不會檢查實參的數量。
- 如果實參的數量多於形參的數量,多餘實參不會被賦值。
- 如果實參的數量少於形參的數量,多餘的形參會被定義為 undefined。表達式的運行結果為 NaN。
程式碼舉例:
function sum(a, b) { console.log(a + b); } sum(1, 2); sum(1, 2, 3); sum(1);
列印結果:
3 3 NaN
注意:在 JS 中,形參的默認值是 undefined。
函數的返回值
舉例:
console.log(sum(3, 4)); // 將函數的返回值列印出來 //函數:求和 function sum(a, b) { return a + b; }
return 的作用是結束方法。
注意:
- return 的值將會作為函數的執行結果返回,可以定義一個變數,來接收該結果。
- 在函數中,return後的語句都不會執行(函數在執行完 return 語句之後停止並立即退出函數)
- 如果return語句後不跟任何值,就相當於返回一個undefined
- 如果函數中不寫return,則也會返回undefined
- 返回值可以是任意的數據類型,可以是對象,也可以是函數。
- return 只能返回一個值。如果用逗號隔開多個值,則以最後一個為準。
函數名、函數體和函數載入問題(重要,請記住)
我們要記住:函數名 == 整個函數。舉例:
console.log(fn) == console.log(function fn(){alert(1)}); //定義fn方法 function fn(){ alert(1) };
我們知道,當我們在調用一個函數時,通常使用函數()
這種格式;可如果,我們是直接使用函數
這種格式,它的作用相當於整個函數。
函數的載入問題:JS載入的時候,只載入函數名,不載入函數體。所以如果想使用內部的成員變數,需要調用函數。
fn() 和 fn 的區別【重要】
-
fn()
:調用函數。調用之後,還獲取了函數的返回值。 -
fn
:函數對象。相當於直接獲取了整個函數對象。
break、continue、return 的區別
- break :結束當前的循環體(如 for、while)
- continue :跳出本次循環,繼續執行下次循環(如 for、while)
- return :1、退出循環。2、返回 return 語句中的值,同時結束當前的函數體內的程式碼,退出當前函數。
立即執行函數
現有匿名函數如下:
function(a, b) { console.log("a = " + a); console.log("b = " + b); };
立即執行函數如下:
(function(a, b) { console.log("a = " + a); console.log("b = " + b); })(123, 456);
立即執行函數:函數定義完,立即被調用,這種函數叫做立即執行函數。
立即執行函數往往只會執行一次。為什麼呢?因為沒有變數保存它,執行完了之後,就找不到它了。
方法
函數也可以成為對象的屬性。如果一個函數是作為一個對象的屬性保存,那麼,我們稱這個函數是這個對象的方法。
調用這個函數就說調用對象的方法(method)。函數和方法,有什麼本質的區別嗎?它只是名稱上的區別,並沒有其他的區別。
函數舉例:
// 調用函數 fn();
方法舉例:
// 調用方法 obj.fn();
我們可以這樣說,如果直接是fn()
,那就說明是函數調用。如果是XX.fn()
的這種形式,那就說明是方法調用。
arguments 的使用
當我們不確定有多少個參數傳遞的時候,可以用 arguments 來獲取。在 JavaScript 中,arguments 實際上是當前函數的一個內置對象。所有函數都內置了一個 arguments 對象(只有函數才有 arguments 對象),arguments 對象中存儲了傳遞的所有實參.
arguments的展示形式是一個偽數組。偽數組具有以下特點:
- 可以進行遍歷;具有數組的 length 屬性。
- 按索引方式存儲數據。
- 不具有數組的 push()、pop() 等方法。
程式碼舉例:利用 arguments 求函數實參中的最大值
程式碼實現:
function getMaxValue() { var max = arguments[0]; // 通過 arguments 遍歷實參 for (var i = 0; i < arguments.length; i++) { if (max < arguments[i]) { max = arguments[i]; } } return max; } console.log(getMaxValue(1, 3, 7, 5));