原創文章&經驗總結&從校招到A廠一路陽光一路滄桑
詳情請戳www.coderccc.com
主要知識點有:函數參數默認值、剩餘參數、擴展運算符、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對象中更新。
-
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(); }
由於閉包會持有外部函數的變量,因此對閉包的尾調用優化很難處理,但是,在遞歸操作中,可以利用到尾調用優化,減少棧幀個數,降低內存。