ES6 函數的擴展

第三章 函數數組

函數形參的默認值閉包

ES5模擬默認參數app

function makeRequest (url, timeout, callback) {
  timeout = timeout || 2000;
  callback = callback || function () {};
}

爲形參賦予默認值,說明了該參數爲可選參數。函數

ES6的默認參數值優化

function makeRequest (url, timeout = 2000, callback = function () {}){
  // 函數執行
}

須要注意的是,在參數中,null也是合法的參數值,因此傳入null的時候不會用默認值替代。this

ES6真正的可選參數是參數後面帶?聲明。url

默認參數值對arguments對象的影響rest

在ES5非嚴格模式中,函數命名參數的變化會體如今arguments對象中。code

在ES5嚴格模式中,不管參數如何變化,arguments對象再也不隨之改變。對象

ES6默認處於嚴格模式,默認參數值的存在使得arguments對象保持與命名參數的分離。

默認參數表達式

默認參數值能夠使用任意數據類型,甚至是函數執行返回的結果。

ES6的參數聲明具備臨時性死區的特性,不能在聲明以前調用。

function add (first = second, second = 1) {
  return first + second;
}
// 這是一個運行時纔會拋出的錯誤,只有當first傳入了非法值,引起默認值賦值的時候,纔會拋出error。
add(1, 2); //3,沒有使用默認值
add();  // ReferenceError,使用默認值時候發如今second聲明前使用了second

處理無命名參數

ES5中的無命名參數

ES5調用arguments對象來獲取全部的實參,從而間接地對無命名參數操做。

不定參數

ES6引入在命名參數前的三個點...來代表不定參數(rest parameters)。全部自它以後傳入的全部參數,都被歸入以這個參數命名的數組中。ES6中arguments依然存在,可是應當儘量地使用不定參數來替代它的做用。

使用限制:

  • 不定參數必定要放在參數表的末尾。
  • 不能用於對象字面量setter中(由於它的參數有且只能有1個)。

加強的Function構造函數

ES6加強了Function構造函數的功能,支持在建立函數時定義默認參數和不定參數。

var add = new Function('first', 'second = 1', 'return first + second;');
add(1, 1);  // 2
add(1);       // 2

name屬性

ES6爲全部函數添加了name屬性,其特性爲:

  • 函數自身的命名,權重最高,不會被覆蓋。
  • 函數表達式所賦值的變量名,權重其次,會覆蓋匿名函數表達式的name。
  • getter/setter會有get/set前綴,bind返回的函數表達式會有bound前綴。
  • Function構造函數建立的函數,名稱爲anonymous。

不能使用name來獲取對於函數的引用。

展開運算符

和不定參數使用相對的是展開運算符,一樣也是...聲明。

它使用在傳遞實參的時候,將一個Array展開爲參數表的形式。

let values = [25, 50, 75, 100];
// 二者等價
Math.max.apply(Math, values);
Math.max(...values);

明確函數的多重用途

JS函數有兩個不一樣的內部方法: [[Call]]和[[Construct]]。

經過new關鍵字調用函數時,執行的是[[Construct]]函數,它負責建立一個實例對象,而後再執行函數體,將this綁定到實例上。

不經過new調用函數,則執行[[Call]]函數,直接執行代碼中的函數體。

ES5判斷函數被調用方法

使用this instanceof class的形式。

function Person (name) {
  // new關鍵字將this綁定到實例
  if(this instanceof Person) {
    this.name = name;
  }
  // [[Call]]調用不會綁定this到實例
  else {
    throw new Error('必須使用new關鍵字調用Person');
  }
}

元屬性(Metaproperty)new.target

new.target表示new操做符的目標,即該構造函數。若是[[Call]]方式調用,則其爲undefined。

塊級函數

ES6引入塊級做用域後,在塊級做用域中聲明的函數只能在塊內使用,當跳出塊後,該函數將沒法訪問。

嚴格模式下,function聲明的塊級函數會被提高到塊級做用域的頂部。

非嚴格模式下,function聲明的跨級函數會被提高到外層的函數做用域。

箭頭函數(Lambda)

與傳統函數的不一樣:

  • 沒有this、super、arguments和new.target綁定,這些值由外層最近一層非箭頭函數決定。
  • 不能經過new關鍵字調用,沒有[[Construct]]方法。
  • 沒有原型。
  • 不能夠改變this綁定(由於始終從外層獲取)。
  • 不支持重複的命名參數。

lambda表達式也有name屬性,相似於函數。

沒有this綁定

lambda表達式的this必須經過查找做用域鏈來決定其值。

箭頭函數的返回值

沒有用{}包裹時,lambda表達式只能有一條執行語句,並返回該語句的返回值。

var result = [].sort((a, b) => a - b);  // 等同於 return a - b

尾遞歸優化(TCO)

function TCO (fn) {
  let value,
      active = false,
      accumulated = [];
  
  // 在f內部進行遞歸調用的時候,其實調用的是這個返回的accumulator
  // 這裏須要注意的是,要將f中的遞歸調用函數名使用accumulator被賦予的變量名
  return function accumulator(...args){
    accumulated.push(args);

    if(!active) {
      active = true;
      while(accumulated.length) {
        /**
        * 因爲每次須要遞歸的fn.apply都會致使accumulated被push進一個新的arguments,
        * 因此這個while一直要到所有執行結束纔會跳出,
        * 但這種結構只會保持最多兩層的調用棧
        */
        value = fn.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  }
}

var sum = TCO(function(x, y) {
  if(y > 0) {
    // 再次調用的實際上是閉包返回的accumulator,而不是這個傳入的function
    return sum(x + 1, y - 1);
  } else {
    return x;
  }
});
相關文章
相關標籤/搜索