箭頭函數(shù)
1.定義對(duì)象和方法
JS 中對(duì)象方法的定義方式是在對(duì)象上定義一個(gè)指向函數(shù)的屬性,當(dāng)方法被調(diào)用的時(shí)候擂涛,方法內(nèi)的 this 就會(huì)指向方法所屬的對(duì)象。
在使用箭頭函數(shù)的時(shí)候需要注意 參考:阮一峰ES6標(biāo)準(zhǔn)入門
(1)函數(shù)體內(nèi)的this對(duì)象紧显,就是定義時(shí)所在的對(duì)象梢睛,而不是使用時(shí)所在的對(duì)象。*
(2)不可以當(dāng)作構(gòu)造函數(shù)伴箩,也就是說入愧,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤嗤谚。*
(3)不可以使用arguments對(duì)象棺蛛,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用巩步,可以用 rest 參數(shù)代替旁赊。
(4)不可以使用yield命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)椅野。
1.1 定義字面量方法
因?yàn)榧^函數(shù)的語(yǔ)法很簡(jiǎn)潔终畅,可能不少同學(xué)會(huì)忍不住用它來(lái)定義字面量方法
const calculator = {
array: [1, 2, 3],
sum: () => {
console.log(this === window); // => true
return this.array.reduce((result, item) => result + item);
}
};
console.log(this === window); // => true
// Throws "TypeError: Cannot read property 'reduce' of undefined"
calculator.sum();
calculator.sum 使用箭頭函數(shù)來(lái)定義,但是調(diào)用的時(shí)候會(huì)拋出 TypeError竟闪,原因是對(duì)象不構(gòu)成單獨(dú)的作用域离福,導(dǎo)致箭頭函數(shù)定義時(shí)的作用域就是全局作用域(箭頭函數(shù)的this永遠(yuǎn)指向上一層),運(yùn)行時(shí) this.array 是未定義的炼蛤,調(diào)用 calculator.sum 的時(shí)候妖爷,執(zhí)行上下文里面的 this 仍然指向的是 window。
上層代碼符合第一條規(guī)定理朋,指向window絮识。
解決的辦法是,使用函數(shù)表達(dá)式或者方法簡(jiǎn)寫(ES6 中已經(jīng)支持)來(lái)定義方法
在這里需要注意的是不要忘記this的定義:關(guān)鍵字 this 總是指向調(diào)用該方法的對(duì)象
const calculator = {
array: [1, 2, 3],
sum() {
console.log(this === calculator); // => true
return this.array.reduce((result, item) => result + item);
}
};
calculator.sum(); // => 6
改成函數(shù)表達(dá)式或者簡(jiǎn)寫的方式(函數(shù)是有自己的作用域暗挑,有屬于自己的this)
1.2 定義原型方法
同樣的規(guī)則適用于原型方法(prototype method)的定義
function Cat(name) {
this.name = name;
}
Cat.prototype.sayCatName = () => {
console.log(this === window); // => true
return this.name;
};
const cat = new Cat('Mew');
cat.sayCatName(); // => undefined
上述代碼道理和上面一樣笋除,箭頭函數(shù)沒有自己的this,cat對(duì)象不能構(gòu)成單獨(dú)的作用域所以箭頭函數(shù)的this指向window.
使用傳統(tǒng)的函數(shù)表達(dá)式就能解決問題
function Cat(name) {
this.name = name;
}
Cat.prototype.sayCatName = function () {
console.log(this === cat); // => true
return this.name;
};
const cat = new Cat('Mew');
cat.sayCatName(); // => 'Mew'
2.定義時(shí)間回調(diào)函數(shù)
this 是 JS 中很強(qiáng)大的特性炸裆,可以通過多種方式改變函數(shù)執(zhí)行上下文垃它,JS 內(nèi)部也有幾種不同的默認(rèn)上下文指向,但普適的規(guī)則是在誰(shuí)上面調(diào)用函數(shù) this 就指向誰(shuí)烹看,這樣代碼理解起來(lái)也很自然国拇,讀起來(lái)就像在說,某個(gè)對(duì)象上正在發(fā)生某件事情惯殊。
但是酱吝,箭頭函數(shù)在聲明的時(shí)候就綁定了執(zhí)行上下文,要?jiǎng)討B(tài)改變上下文是不可能的土思,在需要?jiǎng)討B(tài)上下文的時(shí)候它的弊端就凸顯出來(lái)务热。比如在客戶端編程中常見的 DOM 事件回調(diào)函數(shù)(event listenner)綁定忆嗜,觸發(fā)回調(diào)函數(shù)時(shí) this 指向當(dāng)前發(fā)生事件的 DOM 節(jié)點(diǎn),而動(dòng)態(tài)上下文這個(gè)時(shí)候就非常有用崎岂,比如下面這段代碼試圖使用箭頭函數(shù)來(lái)作事件回調(diào)函數(shù)捆毫。
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
在全局上下文下定義的箭頭函數(shù)執(zhí)行時(shí) this 會(huì)指向 window,當(dāng)單擊事件發(fā)生時(shí)冲甘,瀏覽器會(huì)嘗試用 button 作為上下文來(lái)執(zhí)行事件回調(diào)函數(shù)绩卤,但是箭頭函數(shù)預(yù)定義的上下文是不能被修改的,這樣 this.innerHTML 就等價(jià)于 window.innerHTML江醇,而后者是沒有任何意義的濒憋。
使用函數(shù)表達(dá)式就可以在運(yùn)行時(shí)動(dòng)態(tài)的改變 this,修正后的代碼
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
箭頭函數(shù)可以讓this指向固定化陶夜,這種特性很有利于封裝回調(diào)函數(shù)凛驮,DOM 事件的回調(diào)函數(shù)封裝在一個(gè)對(duì)象里面。
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ù)辐烂,這導(dǎo)致這個(gè)箭頭函數(shù)里面的this,總是指向handler對(duì)象捂贿。否則,回調(diào)函數(shù)運(yùn)行時(shí)胳嘲,this.doSomething這一行會(huì)報(bào)錯(cuò)厂僧,因?yàn)榇藭r(shí)this指向document對(duì)象。
2.1setTimeout()和setInterval()
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
// s2: 0
上面代碼中了牛,Timer函數(shù)內(nèi)部設(shè)置了兩個(gè)定時(shí)器颜屠,分別使用了箭頭函數(shù)和普通函數(shù)。前者的this綁定定義時(shí)所在的作用域(即Timer函數(shù))鹰祸,后者的this指向運(yùn)行時(shí)所在的作用域(即全局對(duì)象)甫窟。所以,3100 毫秒之后蛙婴,timer.s1被更新了 3 次粗井,而timer.s2一次都沒更新。
3.定義構(gòu)造函數(shù)
構(gòu)造函數(shù)中的 this 指向新創(chuàng)建的對(duì)象街图,當(dāng)執(zhí)行 new Car() 的時(shí)候浇衬,構(gòu)造函數(shù) Car 的上下文就是新創(chuàng)建的對(duì)象,也就是說 this instanceof Car === true餐济。顯然耘擂,箭頭函數(shù)是不能用來(lái)做構(gòu)造函數(shù),原因是箭頭函數(shù)根本沒有自己的this絮姆,導(dǎo)致內(nèi)部的this就是外層代碼塊的this醉冤。正是因?yàn)樗鼪]有this秩霍,所以也就不能用作構(gòu)造函數(shù)。
換句話說蚁阳,箭頭構(gòu)造函數(shù)的執(zhí)行并沒有任何意義
const Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
const helloMessage = new Message('Hello World!');
聲明構(gòu)造函數(shù)修復(fù)
const Message = function(text) {
this.text = text;
};
const helloMessage = new Message('Hello World!');
console.log(helloMessage.text); // => 'Hello World!'
4.追求過短的代碼
箭頭函數(shù)允許你省略參數(shù)兩邊的括號(hào)铃绒、函數(shù)體的花括號(hào)、甚至 return 關(guān)鍵詞韵吨,在實(shí)際的軟件工程中匿垄,代碼寫完之后會(huì)被很多工程師閱讀,真正的 write once, read many times归粉,在代碼可讀性方面椿疗,最短的代碼可能并不總是最好的。
一定程度上糠悼,壓縮了太多邏輯的簡(jiǎn)短代碼届榄,閱讀起來(lái)就沒有那么直觀,比如下面的例子
const multiply = (a, b) => b === undefined ? b => a * b : a * b;
const double = multiply(2);
double(3); // => 6
multiply(2, 3); // => 6
正常代碼:箭頭函數(shù)中加上括號(hào)倔喂、條件判斷铝条、返回語(yǔ)句,或者使用普通的函數(shù)
function multiply(a, b) {
if (b === undefined) {
return function (b) {
return a * b;
}
}
return a * b;
}
const double = multiply(2);
double(3); // => 6
multiply(2, 3); // => 6