js-ES6學習筆記-函式的擴充套件
1、ES6函式引數的預設值,直接寫在引數定義的後面。引數變數是預設宣告的,所以不能用let或const再次宣告。
function Point(x = 0, y = 0) { this.x = x; this.y = y; } var p = new Point(); p // { x: 0, y: 0 } function foo(x = 5) { let x = 1; // error const x = 2; // error }
2、通常情況下,定義了預設值的引數,應該是函式的尾引數。因為這樣比較容易看出來,到底省略了哪些引數。如果非尾部的引數設定預設值,實際上這個引數是沒法省略的。除非顯式輸入undefined。
3、指定了預設值以後,函式的length屬性,將返回沒有指定預設值的引數個數。也就是說,指定了預設值後,length屬性將失真。如果設定了預設值的引數不是尾引數,那麼length
屬性也不再計入後面的引數了。rest引數(可變引數,將會放入一個數組中,形如...args)也不會計入length屬性。
4、一旦設定了引數的預設值,函式進行宣告初始化時,引數會形成一個單獨的作用域(context)。等到初始化結束,這個作用域就會消失。這種語法行為,在不設定引數預設值時,是不會出現的。
var x = 1; function f(x, y = x) { console.log(y); } f(2) // 2
上面程式碼中,引數y的預設值等於變數x。呼叫函式y時,引數形成一個單獨的作用域。在這個作用域裡面,預設值變數y指向第一個引數x,而不是全域性變數x,所以輸出是2。
5、利用引數預設值,可以指定某一個引數不得省略,如果省略就丟擲一個錯誤。引數的預設值不是在定義時執行,而是在執行時執行(即如果引數已經賦值,預設值中的函式就不會執行)。
function throwIfMissing() { throw new Error('Missing parameter'); } function foo(mustBeProvided = throwIfMissing()) { return mustBeProvided; } foo() // Error: Missing parameter
另外,可以將引數預設值設為undefined,表明這個引數是可以省略的。
6、ES6 引入 rest 引數(形式為“...變數名”),用於獲取函式的多餘引數,這樣就不需要使用arguments物件了。rest 引數搭配的變數是一個數組,該變數將多餘的引數放入陣列中。rest 引數中的變數代表一個數組,所以陣列特有的方法都可以用於這個變數。注意,rest 引數之後不能再有其他引數(即只能是最後一個引數),否則會報錯。
7、擴充套件運算子(spread)是三個點(...)。它好比 rest 引數的逆運算,將一個數組轉為用逗號分隔的引數序列。(rest引數常用與函式宣告中,該運算子主要用於函式呼叫。)
8、由於擴充套件運算子可以展開陣列,所以不再需要apply方法,將陣列轉為函式的引數了。
9、擴充套件運算子的應用:
- 合併陣列
// ES5 [1, 2].concat(more) // ES6 [1, 2, ...more] var arr1 = ['a', 'b']; var arr2 = ['c']; var arr3 = ['d', 'e']; // ES5的合併陣列 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] // ES6的合併陣列 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]
- 與解構賦值結合
const [first, ...rest] = [1, 2, 3, 4, 5]; first // 1 rest // [2, 3, 4, 5]
如果將擴充套件運算子用於陣列賦值,只能放在引數的最後一位,否則會報錯。
- 函式的返回值
- 字串擴,展運算子還可以將字串轉為真正的陣列。
[...'hello'] // [ "h", "e", "l", "l", "o" ]
上面的寫法,有一個重要的好處,那就是能夠正確識別32位的Unicode字元。
- 實現了Iterator介面的物件,任何Iterator介面的物件,都可以用擴充套件運算子轉為真正的陣列。
var nodeList = document.querySelectorAll('div'); var array = [...nodeList];
-
Map和Set結構,Generator函式
10、ES6允許使用“箭頭”(=>
)定義函式。
var f = v => v; //等於 var f = function(v) { return v; };
如果箭頭函式不需要引數或需要多個引數,就使用一個圓括號代表引數部分。
箭頭函式的一個用處是簡化回撥函式。
函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
this指向的固定化,並不是因為箭頭函式內部有繫結this的機制,實際原因是箭頭函式根本沒有自己的this,導致內部的this就是外層程式碼塊的this。正是因為它沒有this,所以也就不能用作建構函式。
11、ES7提出了“函式繫結”(function bind)運算子,用來取代call、apply、bind呼叫。雖然該語法還是ES7的一個提案,但是Babel轉碼器已經支援。
函式繫結運算子是並排的兩個雙冒號(::),雙冒號左邊是一個物件,右邊是一個函式。該運算子會自動將左邊的物件,作為上下文環境(即this物件),繫結到右邊的函式上面。
foo::bar; // 等同於 bar.bind(foo); foo::bar(...arguments); // 等同於 bar.apply(foo, arguments); const hasOwnProperty = Object.prototype.hasOwnProperty; function hasOwn(obj, key) { return obj::hasOwnProperty(key); }
12、尾呼叫(Tail Call)是函數語言程式設計的一個重要概念,是指某個函式的最後一步是呼叫另一個函式。尾呼叫不一定出現在函式尾部,只要是最後一步操作即可,即return一個函式呼叫。
13、尾呼叫優化:函式呼叫會在記憶體形成一個“呼叫記錄”,又稱“呼叫幀”(call frame),儲存呼叫位置和內部變數等資訊。如果在函式A的內部呼叫函式B,那麼在A的呼叫幀上方,還會形成一個B的呼叫幀。等到B執行結束,將結果返回到A,B的呼叫幀才會消失。如果函式B內部還呼叫函式C,那就還有一個C的呼叫幀,以此類推。所有的呼叫幀,就形成一個“呼叫棧”(call stack)。
尾呼叫由於是函式的最後一步操作,所以不需要保留外層函式的呼叫幀,因為呼叫位置、內部變數等資訊都不會再用到了,只要直接用內層函式的呼叫幀,取代外層函式的呼叫幀就可以了。
這就叫做“尾呼叫優化”(Tail call optimization),即只保留內層函式的呼叫幀。如果所有函式都是尾呼叫,那麼完全可以做到每次執行時,呼叫幀只有一項,這將大大節省記憶體。這就是“尾呼叫優化”的意義。
注意,只有不再用到外層函式的內部變數,內層函式的呼叫幀才會取代外層函式的呼叫幀,否則就無法進行“尾呼叫優化”。
14、函式呼叫自身,稱為遞迴。如果尾呼叫自身,就稱為尾遞迴。遞迴非常耗費記憶體,因為需要同時儲存成千上百個呼叫幀,很容易發生“棧溢位”錯誤(stack overflow)。但對於尾遞迴來說,由於只存在一個呼叫幀,所以永遠不會發生“棧溢位”錯誤。
//非尾遞迴階乘,複雜度 O(n) function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120 //尾遞迴階乘,複雜度O(1) function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5, 1) // 120 //非尾遞迴fib數列 function Fibonacci (n) { if ( n <= 1 ) {return 1}; return Fibonacci(n - 1) + Fibonacci(n - 2); } Fibonacci(10); // 89 // Fibonacci(100) // Fibonacci(500) // 堆疊溢位了 //尾遞迴fib數列 function Fibonacci2 (n , ac1 = 1 , ac2 = 1) { if( n <= 1 ) {return ac2}; return Fibonacci2 (n - 1, ac2, ac1 + ac2); } Fibonacci2(100) // 573147844013817200000 Fibonacci2(1000) // 7.0330367711422765e+208 Fibonacci2(10000) // Infinity
15、尾遞迴的實現,往往需要改寫遞迴函式,確保最後一步只調用自身。做到這一點的方法,就是把所有用到的內部變數改寫成函式的引數。(使用柯里化或者ES6預設值可以保持原本的呼叫方式)
16、ES6的尾呼叫優化只在嚴格模式下開啟,正常模式是無效的。