前端學習(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));