函數(shù)的擴展
2、箭頭函數(shù)
使用注意點
箭頭函數(shù)有幾個注意點:
1、函數(shù)體內(nèi)的this對象,就是定義時所在的對象,而不是使用時所在的對象 (尤其要注意:this對象的指向是可變的,但是在箭頭函數(shù)中,它是固定的)
2、不可以當做構(gòu)造函數(shù),也就是說,不可以使用new命令,否則會拋出一個錯誤
3、不可以使用arguments對象,該對象在函數(shù)體內(nèi)不存在。如果要用,可以用rest參數(shù)代替
4、不可以使用yield命令,因此箭頭函數(shù)不能用作Generator函數(shù)
function foo(){
setTimeout(()=>{
console.log('id:',this.id);
},100);
}
var id = 21;
foo.call({id:42}); /id: 42
如果是普通函數(shù),執(zhí)行時this應該指向全局對象window,這時應該輸出21
但是箭頭函數(shù)導致this總是指向函數(shù)定義生效時所在的對象(本例是{id:42})
箭頭函數(shù)可以讓setTimeout里面的this綁定定義時所在的作用域,而不是指向運行時所在的作用域:
function Timer(){
this.s1=0;
this.s2=0;
//箭頭函數(shù)
setInterval(()=>this.s1++,1000);
//普通函數(shù)
setInterval(function(){
this.s2++;
},1000);
}
var timer=new Timer();
setTimeout(()=>console.log('s1',timer.s1),3100);
setTimeout(()=>console.log('s2',timer.s2),3100);
//s1:3 s1使用的是箭頭函數(shù),this綁定定義時所在的作用域(即Timer函數(shù))
//s2:0 s2使用的是普通函數(shù),this指向運行時所在的作用域(即全局對象
//所以3100毫秒之后,thimer.s1被更新了3次,而timer.s2一次都沒更新。
)
箭頭函數(shù)可以讓this指向固定化,這種特性很有利于封裝回調(diào)函數(shù)。
下面的這個例子DOM事件的回調(diào)函數(shù)封裝在一個對象里面
var handler={
id:'123456',
init:function(){
document.addEventListener('click',
event=> this.doSomething(event.type),false);
},
doSomething:function(type){
console.log('Handling' + type + 'for' + this.id);
}
};
上面代碼的init方法中,使用了箭頭函數(shù),這導致了這個箭頭函數(shù)里面的this,總是指向handler對象。否則回調(diào)函數(shù)執(zhí)行時,this.doSomething這一行會報錯,因為此時this指向document對象
this指向的固定化,并不是因為箭頭函數(shù)內(nèi)部又綁定this的機制,實際原因是函數(shù)根本沒有自己的this,導致內(nèi)部的this就是外層代碼塊的this。正式因為它沒有this,所以也就不能用作構(gòu)造函數(shù)。
所以,箭頭函數(shù)轉(zhuǎn)換成ES5的代碼如下:
//ES6
function foo(){
setTimeout(()=>{
console.log('id:' , this.id);
},100);
}
//ES5
function foo(){
var _this=this;
setTimeout(function(){
console.log('id:',_this.id);
},100);
}
//代碼中轉(zhuǎn)換后的ES5版本清楚的說明了,箭頭函數(shù)里面根本沒有自己的this,而是引用外層的this
function foo(){
return () =>{
return () =>{
return () =>{
console.log('id',this.id);
}
}
}
}
var f=foo.call({id:1});
var t1 = f.call({id:2})()(); //id:1
var t2 = f().call({id:3})(); //id:1
var t3 = f()().call({id:4}); //id:1
//上面代碼中只有一個函數(shù)foo的this,因為所有的內(nèi)層函數(shù)都是箭頭函數(shù),都沒有自己的this,它們的this其實都是最外層foo函數(shù)的this,所以輸出的結(jié)果都是相同的。
箭頭函數(shù)沒有自己的this,所以也就不能用call()、apply()、bind()這些方法去改變this的指向
不適用場合
由于箭頭函數(shù)使得this從“動態(tài)”變成“靜態(tài)”,所以以下兩種場合不應該使用箭頭函數(shù):
第一種場合:是定義函數(shù)的方法,且該方法內(nèi)部包含this
const cat = {
lives:9,
jumps:() => {
this.lives--;
}
}//錯誤
//調(diào)用cat.jumps()方法時,如果是普通函數(shù),該方法內(nèi)部的this指向cat
//如果像上面的箭頭函數(shù),使得this指向全局對象,會得不到預期結(jié)果
第二個場合是需要動態(tài)this的時候,也不應該使用箭頭函數(shù)
var button = document.getElementById('press');
button.addEventListener('click',() =>{
this.classList.toggle('on');
})//報錯
//因為button的監(jiān)聽函數(shù)是一個箭頭函數(shù),導致里面的this就是全局對象。
//如果改成普通函數(shù),this就會動態(tài)指向被點擊的按鈕對象
另外,如果函數(shù)體很復雜,有許多行?;蛘吆瘮?shù)內(nèi)部有大量的讀寫操作,不單純是為了計算值,這時也不應該使用箭頭函數(shù),而是使用普通函數(shù),這樣可以提高代碼可讀性
嵌套的箭頭函數(shù)
箭頭函數(shù)內(nèi)部還可以再使用箭頭函數(shù)