天天看點

深入了解ES6--3.函數1. 函數參數預設值2. 剩餘參數3. 擴充運算符4. new.target屬性5. 塊級函數6. 箭頭函數7. 尾調用優化

主要知識點有:函數參數預設值、剩餘參數、擴充運算符、new.target屬性、塊級函數、箭頭函數以及尾調用優化

1. 函數參數預設值

  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);
     })
    複制代碼
               
  2. 函數參數預設值表達式

    除了直接使用具體值賦給函數參數預設值外,函數參數預設值還可以有表達式構成。

    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),在函數參數預設值表達式中,還未初始化指派的參數值無法作為其他參數的預設值
  3. 函數參數預設值對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. 箭頭函數

箭頭函數提供了一種更加簡潔的函數書寫方式,并且與傳統函數有很多不同的地方。箭頭函數基本的文法為:

參數 => 函數體

根據參數的個數以及函數體的行數有多種變形:

  1. 無參數時,可以使用圓括号()表示;當隻有一個參數時可以省略圓括号();當有多個參數時可以使用圓括号包裹,并參數之間用逗号進行分隔;
  2. 當函數體有多行語句時,使用大括号{}包裹起來,就像寫正常的函數一樣;當隻有一行語句時,并需要傳回結果時,可以省去大括号{},結果會自動傳回。
  3. 如果需要傳回對象的話,需要使用圓括号()将對象包裹起來,為了防止對象字面量被認為是函數體語句。

箭頭函數的特性:

  1. 沒有this、super、arguments,new.target綁定:this、super、arguments以及内部函數的new.target的值由所在的最近的外部非箭頭函數來決定;
  2. 沒有arguments對象綁定,但是能夠通路包含它的外部函數的arguments對象;
    let outer = function(arg){
     	return ()=>arguments[0];
     }
     let inner = outer(7);
     console.log(inner()) //7
    複制代碼
               
  3. 不能使用new來調用:箭頭函數沒有[[Construct]]方法,是以不能被用為構造函數,使用new調用函數會抛出錯誤;
  4. 沒有原型:沒有使用new,是以沒有prototype屬性;
  5. 不能修改this:不能通過call(),apply()以及bind()方法修改this;
  6. 不允許使用重複的具名參數:箭頭函數不允許擁有重複的具名參數,無論是否在嚴格模式下;而傳統函數隻有在嚴格模式下才禁止使用重複的具名參數;

7. 尾調用優化

尾調用是指在一個函數内最後的語句調用了另外一個函數,這個函數會重新建立新的棧幀并置于調用棧之上,如果調用次數過多,會導緻記憶體過大。當滿足以下條件時,執行引擎會針對尾調用進行優化,不再重新建立新的棧幀,而是會複用目前棧幀:

  1. 尾調用不能引用目前棧幀中的變量;
  2. 進行尾調用的函數在尾調用傳回結果後不能做額外任何操作;
  3. 尾調用的結果作為目前函數的傳回值;
    //尾遞歸優化
     'use strict'
     
     function doSomething(){
     	//滿足尾調用優化條件
     	return doElse();
     }	
     
     
     function doSomething(){
     	let tmp = doElse();
     	//尾調用函數結果沒有直接傳回,是以不滿足
     	//尾調用優化條件
     	return tmp;
     }	
     
     
     function doSomething(){
     	//尾調用函數後有其他額外操作,結果沒有
     	//立即傳回,是以不滿足尾調用優化條件
     	return 1+doElse();
     }
    複制代碼
               

由于閉包會持有外部函數的變量,是以對閉包的尾調用優化很難處理,但是,在遞歸操作中,可以利用到尾調用優化,減少棧幀個數,降低記憶體。