什么時(shí)候不可用箭頭函數(shù) ()=> ...

箭頭函數(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()

代碼參考:阮一峰ES6標(biāo)準(zhǔn)入門

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

5.致謝

感謝作者:zyscn 席噩、 阮一峰

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末班缰,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子悼枢,更是在濱河造成了極大的恐慌埠忘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馒索,死亡現(xiàn)場(chǎng)離奇詭異莹妒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)绰上,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門旨怠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蜈块,你說我怎么就攤上這事鉴腻。” “怎么了疯趟?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵拘哨,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我信峻,道長(zhǎng)倦青,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任盹舞,我火速辦了婚禮产镐,結(jié)果婚禮上隘庄,老公的妹妹穿的比我還像新娘。我一直安慰自己癣亚,他們只是感情好丑掺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著述雾,像睡著了一般街州。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上玻孟,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天唆缴,我揣著相機(jī)與錄音,去河邊找鬼黍翎。 笑死面徽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的匣掸。 我是一名探鬼主播趟紊,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼碰酝!你這毒婦竟也來(lái)了霎匈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤送爸,失蹤者是張志新(化名)和其女友劉穎唧躲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碱璃,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年饭入,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嵌器。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谐丢,死狀恐怖爽航,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情乾忱,我是刑警寧澤讥珍,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站窄瘟,受9級(jí)特大地震影響衷佃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蹄葱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一氏义、第九天 我趴在偏房一處隱蔽的房頂上張望锄列。 院中可真熱鬧,春花似錦惯悠、人聲如沸邻邮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)筒严。三九已至,卻和暖如春情萤,著一層夾襖步出監(jiān)牢的瞬間鸭蛙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工紫岩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留规惰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓泉蝌,卻偏偏與公主長(zhǎng)得像歇万,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子勋陪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354