es6之箭頭函式
箭頭函式
es6的箭頭函式,顧名思義箭頭函式是使用一個箭頭( => )來定義的函式,這很容易理解但是它有很多行為與傳統的js函式不同:
- 沒有 this 、 super 、 arguments 。
- 不能被使用 new 呼叫: 箭頭函式沒有 [[Construct]] 方法,因此不能被用為建構函式,使用 new 呼叫箭頭函式會丟擲錯誤。
- 沒有原型: 既然不能對箭頭函式使用 new ,那麼它也不需要原型,也就是沒有prototype 屬性。
- 不能更改 this : this 的值在函式內部不能被修改,在函式的整個生命週期內其值會保持不變。
- 沒有 arguments 物件: 既然箭頭函式沒有 arguments 繫結,你必須依賴於具名引數或剩餘引數來訪問函式的引數
- 不允許重複的具名引數: 箭頭函式不允許擁有重複的具名引數,無論是否在嚴格模式下;而相對來說,傳統函式只有在嚴格模式下才禁止這種重複
箭頭函式的語法
var reflect = value => value; // 有效等價於: var reflect = function(value) { return value; };
函式需要傳入多個引數:
var sum = (num1, num2) => num1 + num2; // 有效等價於: var sum = function(num1, num2) { return num1 + num2; };
如果函式沒有任何引數,那麼在宣告時就必須使用一對空括號,就像這樣:
var getName = () => "Nicholas"; // 有效等價於: var getName = function() { return "Nicholas"; };
你基本可以將花括號內部的程式碼當做傳統函式那樣對待,除了 arguments 物件不可用之外。
若你想建立一個空函式,就必須使用空的花括號,就像這樣:
var doNothing = () => {}; // 有效等價於: var doNothing = function() {};
花括號被用於表示函式的主體,它在你至今看到的例子中都工作正常。但若箭頭函式想要從
函式體內向外返回一個物件字面量,就必須將該字面量包裹在圓括號內,例如:
var getTempItem = id => ({ id: id, name: "Temp" }); // 有效等價於: var getTempItem = function(id) { return { id: id, name: "Temp" }; };
沒有this繫結
JS 最常見的錯誤領域之一就是在函式內的 this 繫結。由於一個函式內部的 this 值可以
被改變,這取決於呼叫該函式時的上下文,因此完全可能錯誤地影響了一個物件,儘管你本
意是要修改另一個物件。
箭頭函式沒有 this 繫結,意味著箭頭函式內部的 this 值只能通過查詢作用域鏈來確定。
如果箭頭函式被包含在一個非箭頭函式內,那麼 this 值就會與該函式的相等;否則,
this 值就會是全域性物件(在瀏覽器中是 window ,在 nodejs 中是 global )。
沒有arguments繫結
儘管箭頭函式沒有自己的 arguments 物件,但仍然能訪問包含它的函式的 arguments 對
象。無論此後箭頭函式在何處執行,該物件都是可用的。例如:
function createArrowFunctionReturningFirstArg() { return () => arguments[0]; } var arrowFunction = createArrowFunctionReturningFirstArg(5); console.log(arrowFunction()); // 5
也像對其他函式那樣,你仍然可以對箭頭函式使用 call() 、 apply() 與 bind() 方法,雖
然函式的 this 繫結並不會受影響。這裡有幾個例子:
var sum = (num1, num2) => num1 + num2; console.log(sum.call(null, 1, 2)); // 3 console.log(sum.apply(null, [1, 2])); // 3 var boundSum = sum.bind(null, 1, 2); console.log(boundSum()); // 3
尾呼叫優化
在 ES6 中對函式最有趣的改動或許就是一項引擎優化,它改變了尾部呼叫的系統。尾呼叫(
tail call )指的是呼叫函式的語句是另一個函式的最後語句,就像這樣:
function doSomething() { return doSomethingElse(); // 尾呼叫 }
在 ES5 引擎中實現的尾呼叫,其處理就像其他函式呼叫一樣:一個新的棧幀( stack frame
)被建立並推到呼叫棧之上,用於表示該次函式呼叫。這意味著之前每個棧幀都被保留在內
存中,當呼叫棧太大時會出問題。
那什麼時候不會被優化呢/
- 一個小改動——不返回結果(缺少return),就會產生一個無法被優化的函式:
"use strict"; function doSomething() { // 未被優化:缺少 return doSomethingElse(); }
- 如果你的函式在尾呼叫返回結果之後進行了額外操作,那麼該函式也無法被優化:
"use strict"; function doSomething() { // 未被優化:在返回之後還要執行加法 return 1 + doSomethingElse(); }
- 關閉優化的另一個常見方式,是將函式呼叫的結果儲存在一個變數上,之後才返回了結果,就像這樣:
"use strict"; function doSomething() { // 未被優化:呼叫並不在尾部 var result = doSomethingElse(); return result; }
本例之所以不能被優化,是因為 doSomethingElse() 的值並沒有立即被返回。
- 使用閉包或許就是需要避免的最困難情況,因為閉包能夠訪問上層作用域的變數,會導致尾呼叫優化被關閉。例如:
"use strict"; function doSomething() { var num = 1, func = () => num; // 未被優化:此函式是閉包 return func(); }
"use strict";
function doSomething() {
var num = 1,
func = () => num;
// 未被優化:此函式是閉包
return func();
}
此例中閉包 func() 需要訪問區域性變數 num ,雖然呼叫 func() 後立即返回了其結果,但 是對於 num 的引用導致優化不會發生.