ES6系列之函式
1.函式引數的預設值
ES6允許為函式的引數設定預設值,即直接寫在引數定義的後面。
function log(x, y = 'World') { console.log(x, y); } log('Hello') // Hello World log('Hello', 'China') // Hello China log('Hello', '') // Hello
使用引數預設值時,函式不能有同名引數。
另外,一個容易忽略的地方是,引數預設值不是傳值的,而是每次都重新計算預設值表示式的值。也就是說,引數預設值是惰性求值的 。
與解構賦值預設值結合使用
引數預設值可以與解構賦值的預設值,結合起來使用。
function foo({x, y = 5}) { console.log(x, y); } foo({}) // undefined 5 foo({x: 1}) // 1 5 foo({x: 1, y: 2}) // 1 2 foo() // TypeError: Cannot read property 'x' of undefined
函式的length屬性(函式預期傳入引數的個數)
指定了預設值以後,函式的length屬性,將返回沒有指定預設值的引數個數。也就是說,指定了預設值後,length屬性將失真。
作用域
一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域(context)。等到初始化結束,這個作用域就會消失。
2.rest引數
ES6引入rest引數(形式為…變數名),用於獲取函式的多餘引數,這樣就不需要使用arguments物件了。rest引數搭配的變數是一個數組,該變數將多餘的引數放入陣列中。
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10 // arguments變數的寫法 function sortNumbers() { return Array.prototype.slice.call(arguments).sort(); } // rest引數的寫法 const sortNumbers = (...numbers) => numbers.sort();
注意,rest引數之後不能再有其他引數(即只能是最後一個引數),否則會報錯。
函式的length屬性,不包括rest引數
4.name屬性
函式的name屬性,返回該函式的函式名。
function建構函式返回的函式例項,name屬性的值為anonymous。
bind返回的函式,name屬性值會加上bound字首。
5.箭頭函式
如果箭頭函式不需要引數或需要多個引數,就使用一個圓括號代表引數部分。
如果箭頭函式的程式碼塊部分多於一條語句,就要使用大括號
將它們括起來,並且使用return語句返回。
var sum = (num1, num2) => { return num1 + num2; }
由於大括號被解釋為程式碼塊,所以如果箭頭函式直接返回一個物件,必須在物件外面加上括號,否則會報錯。
// 報錯 let getTempItem = id => { id: id, name: "Temp" }; // 不報錯 let getTempItem = id => ({ id: id, name: "Temp" });
箭頭函式與rest引數結合的例子
const numbers = (...nums) => nums; numbers(1, 2, 3, 4, 5) // [1,2,3,4,5] const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1, 2, 3, 4, 5) // [1,[2,3,4,5]]
箭頭函式有幾個使用注意點。
(1)函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
(2)不可以當做建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。
(3)不可以使用arguments物件,該物件在函式體內不存在。如果要用,可以用rest引數代替。
(4)不可以使用yield命令,因此箭頭函式不能用作Generator函式。
this指向的固定化,並不是因為箭頭函式內部有繫結this的機制,實際原因是箭頭函式根本沒有自己的this,導致內部的this就是外層程式碼塊的this。正是因為它沒有this,所以也就不能用作建構函式。
6.尾呼叫
尾呼叫(tail call)是函數語言程式設計的一個重要概念,本身非常簡單,一句話就能說清楚,就是指某個函式的最後一步是呼叫另一個函式。
尾呼叫之所以與其他呼叫不同,就在於它的特殊的呼叫位置。
我們知道,函式呼叫會在記憶體形成一個“呼叫記錄”,又稱“呼叫幀”(call frame),儲存呼叫位置和內部變數等資訊。如果在函式A的內部呼叫函式B,那麼在A的呼叫幀上芳,就形成一個B的呼叫幀。等到B執行結束,將結果返回到A,B的呼叫幀才會消失。如果函式B內部還呼叫函式C,那就還有一個C的呼叫幀,以此類推。所有的呼叫幀,就形成一個“呼叫棧”(call stack)。
尾呼叫由於是函式的最後一步操作,所以不需要保留外層函式的呼叫幀,因為呼叫位置、內部區域性變數等資訊都不會再用到了,只要直接用內層函式的呼叫幀,取代外層函式的呼叫幀就可以了。
函式呼叫自身,稱為遞迴。如果尾呼叫自身,就稱為尾遞迴。
遞迴非常耗費記憶體,因為需要同時儲存成千上百個呼叫幀,很容易發生“棧溢位”(stack overflow)。但對於尾遞迴來說,由於只存在一個呼叫幀,多以永遠不會發生“棧溢位”錯誤。