廖雪峰JS學習總結-函式篇

yellowlemon發表於2017-03-10

最近在刷廖雪峰的JS教程,把裡面的自己不太清楚的東西在刷一遍。
教程網址:www.liaoxuefeng.com/wiki/001434…

函式的定義和呼叫:

  1. 如果沒有return語句,函式執行完畢後也會返回結果,只是結果為undefined
  2. JavaScript允許傳入任意引數,即使函式內部不需要這些引數,也不影響呼叫。
  3. JavaScript還有一個免費贈送的關鍵字arguments,它只在函式內部起作用,並且永遠指向當前函式的呼叫者傳入的所有引數。arguments類似Array但它不是一個Array
  4. 利用arguments即使函式不定義任何引數,還是可以拿到引數的值。
    function abs() {
     if (arguments.length === 0) {
         return 0;
     }
     var x = arguments[0];
     return x >= 0 ? x : -x;
    }
    abs(); // 0
    abs(10); // 10
    abs(-9); // 9複製程式碼
  5. ES6引入了新的rest引數,他可以獲得額外的rest引數,rest引數只能寫在最後,前面用...標識,從執行結果可知,傳入的引數先繫結a、b,多餘的引數以陣列形式交給變數rest,所以,不再需要arguments我們就獲取了全部引數。(如果傳入的引數連正常定義的引數都沒填滿,也不要緊,rest引數會接收一個空陣列(注意不是undefined))
    function foo(a, b, ...rest) {
     console.log('a = ' + a);
     console.log('b = ' + b);
     console.log(rest);
    }
    foo(1, 2, 3, 4, 5);
    // 結果:
    // a = 1
    // b = 2
    // Array [ 3, 4, 5 ]
    foo(1);
    // 結果:
    // a = 1
    // b = undefined
    // Array []複製程式碼
  6. return語句必須在一行上,如果必須為多行返回,需要在retrun後緊跟 {,正確的多行寫法如下:
    function foo() {
     return { // 這裡不會自動加分號,因為{表示語句尚未結束
         name: 'foo'
     };
    }複製程式碼

變數作用域:

  1. 如果用var在函式體內宣告變數,該變數作用域為整個函式;因為這個原因,在不同函式內部用var申明的同名變數互相獨立,互不影響。
  2. 由於函式可以相互巢狀,內部函式可以訪問外部函式定義的變數,但是如果內部和外部都定義了同名變數怎麼辦了?JS函式查詢變數會從自身定義函式開始,也就是從’內’向’外’,順著作用域鏈查詢,如果內部函式已經定義了,則會’遮蔽’掉外部函式變數。
  3. JS函式的var定義有個特點,它會先掃描整個函式體的語句,把所有var申明的變數“提升”到函式頂部,所以定義變數時,我們應該嚴格遵守在函式內部先宣告的規則。
  4. JavaScript預設有一個全域性物件window,全域性作用域的變數實際上被繫結到window的一個屬性上了。
  5. 為了防止造成命名衝突,比較好的方法是把自己的所有變數和函式,都繫結到一個全域性變數中:
    // 唯一的全域性變數MYAPP:
    var MYAPP = {};
    // 其他變數:
    MYAPP.name = 'myapp';
    MYAPP.version = 1.0;
    // 其他函式:
    MYAPP.foo = function () {
     return 'foo';
    };複製程式碼
  6. 在for迴圈是無法定義具有區域性作用域的的變數的。在ES6中可以使用let替代var可以申明一個塊級作用域的變數。
  7. 對於常量的定義,以前都是使用全部大寫來規定,現在ES6總可以使用關鍵字const來定義常量,constlet都具有塊級作用域,並且const定義以後無法修改。
  8. 補充說明一下letconst的特點,不存在變數提升、會造成暫時性死區、不允許重複定義。

方法:

在一個物件中繫結函式,稱為這個物件的方法。這裡要介紹一個很坑爹的概念,就是this的指向問題。

那麼對於不同的呼叫,這個this的指向分別是什麼了??
答:this的指向在函式定義的時候是確定不了的,只有函式執行的時候才能確定this到底指向誰,實際上this的最終指向的是那個呼叫它的物件。
在《javascript語言精髓》中大概概括了4種呼叫方式:

1. 方法呼叫模式
2. 函式呼叫模式
3. 構造器呼叫模式
4. apply/call呼叫模式複製程式碼

特別補充:

  1. 在構造器呼叫時,如果加入了return並且return了一個物件,this會指向這個return的物件。
  2. 嚴格模式下在函式內部定義的函式,this指向undefined(在非strict模式下,它指向全域性物件window

那麼有沒有辦法去控制this的指向了?
有的,可以使用函式本身的apply方法,它接收兩個引數,第一個引數就是需要繫結的this變數,第二個引數是Array,表示函式本身的引數。另一個與apply()類似的方法是call(),唯一區別是:

  • apply()把引數打包成Array再傳入;
  • call()把引數按順序傳入。
    另外ES5還加入了一個bind()方法,它會建立一個函式的例項,其this值會被繫結到傳給bind()函式的值。

利用apply(),我們還可以動態改變函式的行為。
JavaScript的所有物件都是動態的,即使內建的函式,我們也可以重新指向新的函式。

高階函式:

  1. 一個函式接收另一個函式作為引數,這種函式就稱之為高階函式。
  2. forEach方法,是最基本的方法,就是遍歷與迴圈,預設有3個傳參:分別是遍歷的陣列內容item、陣列索引index、和當前遍歷陣列Array。另外,除去第一個必須的回撥函式引數,還可以接受一個上下文引數(改變回撥函式的this指向);並且forEach不會遍歷空元素。
  3. map方法,基本用法與forEach一致,但是不同的,它會返回一個新的陣列,所以在callback需要有return值,如果沒有,會返回undefined。(從字面理解,map就是對映的意思)
  4. filter方法,用法和map很相似,從字面理解,就是過濾、篩選的意思。但是函式的callback需要返回布林值truefalse,並且返回值只需要為弱等==即可。
  5. some 方法,對陣列中每一項執行指定函式,如果該函式對任一項返回true,則返回true。(一旦遇到true,就會中斷迴圈,返回true,類似於||判斷)
  6. every方法,對陣列中的每一項執行給定函式,如果該函式對每一項返回true,則返回true。(一旦遇到false,就會中斷迴圈,返回false,類似於&&判斷)
  7. sort方法,預設把所有元素先轉換為String再進行ASCII碼排序,所以這個很坑爹。要想正確的排序,需要新增一個函式。sort方法,可以接受一個回撥函式,預設有兩個傳參:分別是比較的陣列項。

閉包(還要在研究一下,以後補充吧):

「閉包」,是指那些能夠訪問獨立(自由)變數的函式(變數在本地使用,但定義在一個封閉的作用域中)。換句話說,這些函式可以“記憶”它被建立時候的環境。特性:

  1. 函式巢狀函式
  2. 函式內部可以引用外部的引數和變數
  3. 引數和變數不會被垃圾回收機制回收

箭頭函式:

  1. 箭頭函式使得表達更加簡潔。
  2. 函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
  3. 不可以當作建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。
  4. 不可以使用arguments物件,該物件在函式體內不存在。如果要用,可以用Rest引數代替。
  5. 不可以使用yield命令,因此箭頭函式不能用作Generator函式。

Generator:

  1. Generator 函式是協程在 ES6 的實現,最大特點就是可以交出函式的執行權(即暫停執行)。
  2. next法返回值的value屬性,是 Generator 函式向外輸出資料;next方法還可以接受引數,向 Generator 函式體內輸入資料。
  3. Generator 函式內部還可以部署錯誤處理程式碼,捕獲函式體外丟擲的錯誤。

有錯誤希望指出,一定會及時修改!

相關文章