主要知識點有:函數參數預設值、剩餘參數、擴充運算符、new.target屬性、塊級函數、箭頭函數以及尾調用優化
1. 函數參數預設值
- 函數參數預設值
let defaultFunc = function(url,tomeout=2000,callback={}){}; //使用預設的timeout和callback defaultFunc('/url'); //使用預設的callback defaultFunc('/url',100); //使用指定的timeout和callback defaultFunc('/url',100,function(body){ doSomething(body); }) 複制代碼
函數參數預設值的指定順序可以随意
//需要指定預設參數的可選參數timeout,排在callback let defaultFunc = function(url,tomeout=2000,callback){}; 複制代碼
隻有在未傳遞參數,或者參數為undefined時,才會使用預設參數,null值被認為是有效的
//null被認為是有效的,不會使用timeout的預設值 defaultFunc('/url',null,function(body){ doSomething(body); }) 複制代碼
-
函數參數預設值表達式
除了直接使用具體值賦給函數參數預設值外,函數參數預設值還可以有表達式構成。
function getValue(){return 5}; function test(a,b=getValue()){ return a+b; } //調用getValue console.log(test(1)); //6 //不使用b的預設值s console.log(test(1,2)); //3 複制代碼
可以使用前面的參數,來作為後面參數的預設值。前面的參數預設值不能引用後面的參數值
function add(a,b=1){return a+b}; console.log(add(1,1)); //2 function addition(a=b,b){return a+b}; console.log(addition(undefined,1)); //Uncaught ReferenceError: b is not defined 複制代碼
函數參數預設值存在暫時性死區(TDZ),在函數參數預設值表達式中,還未初始化指派的參數值無法作為其他參數的預設值
- 函數參數預設值對arguments對象的影響
- ES5中,在非嚴格模式下,arguments對象能夠反映出具名參數的變化,當具名參數值更新的時候,arguments對象中相應的元素值也會更新。在嚴格模式下不能反映出具名參數的變化
function testArgs(a,b){ console.log(a===arguments[0]); //true console.log(b===arguments[1]); //true a='c'; b='d'; console.log(a===arguments[0]); //true console.log(b===arguments[1]); //true } testArgs('a','b'); 複制代碼
- 在ES6中,參數預設值與arguments對象分離,即被賦予參數預設值的參數無法從arguments對象中擷取。另外,無論是非嚴格模式下,還是在嚴格模式下,具名參數的更改都不會在arguments對象中更新。
- ES5中,在非嚴格模式下,arguments對象能夠反映出具名參數的變化,當具名參數值更新的時候,arguments對象中相應的元素值也會更新。在嚴格模式下不能反映出具名參數的變化
2. 剩餘參數
ES6中,當參數個數無法确定時,可以使用剩餘參數(rest parameter)來表示,剩餘參數就相當于一個容器,調用函數時傳入幾個參數值,這個容器就裝載幾個參數值。剩餘參數能夠将多個獨立的參數合并到一個數組中去,剩餘參數表示為
...keys
,有三個點加上一個具名參數辨別符組成。
function rp(...keys){console.log(keys.length)}
rp(1,2); //2
複制代碼
具名參數隻能放在參數組最後面,并且隻能有且僅有一個剩餘參數。剩餘參數不能作為對象字面量的setter屬性
let person ={
set name(...names); //Setter function argument must not be a rest parameter
}
複制代碼
在Function構造器中能夠将函數體以字元串的形式作為函數的參數,并且支援參數預設值以及剩餘參數
var add = new Function("first","second","return first+second");
console.log(add(1,1)); //2
複制代碼
3. 擴充運算符
擴充運算符能夠将數組分離,将分割後單獨的參數值傳遞給函數,能夠替代apply()方法的使用。
console.log(Math.min(0,...[1,2,3,4])); //0
複制代碼
4. new.target屬性
能夠使用
new.target
屬性來判斷函數是否利用
new
來進行調用
function target(){
if(new.target!=='undefined'){
console.log('通過new來調用');
}else{
console.log('不是通過new來調用');
}
}
複制代碼
5. 塊級函數
在代碼塊中能夠聲明函數,函數也被稱之為塊級函數,在嚴格模式下,塊級函數會提升到目前所處代碼塊的頂部,在整個代碼塊中都能夠被通路,在代碼塊外的地方就不能被通路。而在非嚴格模式下,塊級函數會被提升到全局作用域。
6. 箭頭函數
箭頭函數提供了一種更加簡潔的函數書寫方式,并且與傳統函數有很多不同的地方。箭頭函數基本的文法為:
參數 => 函數體
。
根據參數的個數以及函數體的行數有多種變形:
- 無參數時,可以使用圓括号()表示;當隻有一個參數時可以省略圓括号();當有多個參數時可以使用圓括号包裹,并參數之間用逗号進行分隔;
- 當函數體有多行語句時,使用大括号{}包裹起來,就像寫正常的函數一樣;當隻有一行語句時,并需要傳回結果時,可以省去大括号{},結果會自動傳回。
- 如果需要傳回對象的話,需要使用圓括号()将對象包裹起來,為了防止對象字面量被認為是函數體語句。
箭頭函數的特性:
- 沒有this、super、arguments,new.target綁定:this、super、arguments以及内部函數的new.target的值由所在的最近的外部非箭頭函數來決定;
- 沒有arguments對象綁定,但是能夠通路包含它的外部函數的arguments對象;
let outer = function(arg){ return ()=>arguments[0]; } let inner = outer(7); console.log(inner()) //7 複制代碼
- 不能使用new來調用:箭頭函數沒有[[Construct]]方法,是以不能被用為構造函數,使用new調用函數會抛出錯誤;
- 沒有原型:沒有使用new,是以沒有prototype屬性;
- 不能修改this:不能通過call(),apply()以及bind()方法修改this;
- 不允許使用重複的具名參數:箭頭函數不允許擁有重複的具名參數,無論是否在嚴格模式下;而傳統函數隻有在嚴格模式下才禁止使用重複的具名參數;
7. 尾調用優化
尾調用是指在一個函數内最後的語句調用了另外一個函數,這個函數會重新建立新的棧幀并置于調用棧之上,如果調用次數過多,會導緻記憶體過大。當滿足以下條件時,執行引擎會針對尾調用進行優化,不再重新建立新的棧幀,而是會複用目前棧幀:
- 尾調用不能引用目前棧幀中的變量;
- 進行尾調用的函數在尾調用傳回結果後不能做額外任何操作;
- 尾調用的結果作為目前函數的傳回值;
//尾遞歸優化 'use strict' function doSomething(){ //滿足尾調用優化條件 return doElse(); } function doSomething(){ let tmp = doElse(); //尾調用函數結果沒有直接傳回,是以不滿足 //尾調用優化條件 return tmp; } function doSomething(){ //尾調用函數後有其他額外操作,結果沒有 //立即傳回,是以不滿足尾調用優化條件 return 1+doElse(); } 複制代碼
由于閉包會持有外部函數的變量,是以對閉包的尾調用優化很難處理,但是,在遞歸操作中,可以利用到尾調用優化,減少棧幀個數,降低記憶體。