ES6學習之函數擴展
函數默認參數
function test(x = 1, y = 2) { return x + y } test(5, 6) //11 test() //3
若默認參數在必須參數之前,要想取得默認參數,只有當傳入的值為undefined才能取到
function test(x = 1, y) { console.log(x,y) } test(5, 6) //5,6 test(1) //3 undefined test(null,1) //null 1 test(undefined,1) //1,1
參數默認值是惰性求值
let x = 99; //每次調用函數,默認值x+1都會重新計算,而不是默認等於100function foo(p = x + 1) { console.log(p) } foo() //100 x = 105; foo() //106
解構賦值與默認值聯合使用
function test({x,y=5}={}){ console.log(x,y) } test({x:2,y:6}) //2 6 test({}) //undefined 5
上面代碼:如果沒有傳參數,優先使用默認值,若沒有默認值,再使用解構賦值
函數的length屬性(length
屬性的含義是,該函數預期傳入的參數個數。指定了默認值以後,函數的length
屬性,將返回沒有指定默認值的參數個數。)
(function(a) {}).length // 1 (function (a = 5) {}).length // 0 (function (a, b, c = 5) {}).length // 2
作用域(一旦設置了參數的默認值,函數進行聲明初始化時,參數會形成一個單獨的作用域(context)。等到初始化結束,這個作用域就會消失。這種語法行為,在不設置參數默認值時,是不會出現的。)
var x = 1; var z = 2; //函數默認參數的變量優先取函數參數內向同名稱變量的值,若函數參數內部存在同名變量,則取父及變量 function f(x, y = x, m = z) { console.log(y); console.log(m) } f(2) // 2 2
rest參數( rest 參數(形式為...變量名
),用於獲取函數的多余參數。rest 參數搭配的變量是一個數組,該變量將多余的參數放入數組中。)
function add(...values) { let sum = 0; for (var val of values) { sum += val; } return sum; } add(2, 5, 3) // 10
rest 參數之後不能再有其他參數(即只能是最後一個參數),否則會報錯
//error function f(a, ...b, c) { // ... }
嚴格模式(函數內可以使用嚴格模式,但是只要函數參數使用了默認值、解構賦值、或者擴展運算符,就不能在函數內使用嚴格模式)
function doSomething(a, b) { ‘use strict‘; // code }
可以使用全局聲明嚴格模式或把函數包裹在一個無參數的立即執行的函數裏可以規避嚴格模式的限制
‘use strict‘; function doSomething(a, b = a) { // code }
const doSomething = (function () { ‘use strict‘; return function(value = 42) { return value; }; }());
name屬性(返回函數名)
var f = function(){} f.name; //f function test(){} test.name //test
箭頭函數
var f = v => v; //相當於 var f = function (v) { return v }
對於沒有參數或有多個參數的箭頭函數,參數部分用括號括起來。如果返回的代碼塊部分多以一條語句,也要用括號括起來且必須使用return語句
var f = () => 5 f(); //5 var fn = (x, y) => x + y fn(2, 3) //5
var f = ()=>{ console.log("hello,ES6"); return 2; } f() //hello,ES6 2
如果箭頭函數直接返回一個對象,必須在對象外面加上括號,否則會報錯。
// 報錯 let getTempItem = id => { id: id, name: "Temp" }; // 不報錯 let getTempItem = id => ({ id: id, name: "Temp" });
使用箭頭函數請註意:
(1)函數體內的this
對象,就是定義時所在的對象,而不是使用時所在的對象。
(2)不可以當作構造函數,也就是說,不可以使用new
命令,否則會拋出一個錯誤。
(3)不可以使用arguments
對象,該對象在函數體內不存在。如果要用,可以用 rest 參數代替。
(4)不可以使用yield
命令,因此箭頭函數不能用作 Generator 函數。
function foo() { setTimeout(() => { console.log(‘id:‘, this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // id: 42
尾調用及尾調用優化
- 尾調用(某個函數的最後一步是調用另一個函數)
function f(x){ return g(x); } //註意這裏一定要寫return才能表示尾調用
- 尾調用優化(只保留內層函數的調用幀,註意:只有內層函數不再用到外部函數的變量時才能用尾調用優化)
function f() { let m = 1; let n = 2; return g(m + n); } f(); // 等同於 function f() { return g(3); } f(); // 等同於 g(3);
尾遞歸(函數調用自身,稱為遞歸。如果尾調用自身,就稱為尾遞歸)
//普通遞歸函數,容易發生棧溢出 function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120 //尾遞歸函數,內存中永遠只保留最後一幀,永遠不會發生棧溢出 function factorialV2(n, total) { if (n === 1) return total; return factorialV2(n - 1, n * total) } factorialV2(5, 1) //120
尾遞歸的調用方式優化(使用默認參數或者再使用另外一個函數包裹)
function factorialV2(n, total = 1) { if (n === 1) return total; return factorialV2(n - 1, n * total) } function factorial(n){ return factorialV2(n, 1) } factorialV2(5) //120
ES6學習之函數擴展