什么是ES6箭頭函數(shù)苗傅?與普通函數(shù)主要區(qū)別在哪里抒线?到底該不該使用箭頭函數(shù)?

箭頭函數(shù)是匿名函數(shù)渣慕,ES5匿名函數(shù)的語法糖嘶炭;但又增加了ES5所沒有的一些優(yōu)點(diǎn)抱慌,接下來我們一起來看一看箭頭函數(shù)

// ES5
vat tt = function tt() {
  return 55 + 99;
}

// ES6
vat tt = () => 55 + 99
// 是不是一對(duì)比,寫法的差異就看出來了

ES6增加了箭頭函數(shù):

let func = value => value;
// 相當(dāng)于
let func = function () {
  return value;
}
// 如果需要給函數(shù)傳入多個(gè)參數(shù):
let func = (value, num) => value * num;
// 如果函數(shù)的代碼塊需要多條語句:
let func = (value, num) => {
  return value * num;
}
// 如果需要直接返回一個(gè)對(duì)象:
let func = (value, num) => ({total: value * num});
// 與變量解構(gòu)結(jié)合
let func = ({value, num}) => ({total: value * num})
// 使用
var result = func({
  value: 10,
  num: 10
})
console.log(result);  // {total: 100}

以上是箭頭函數(shù)的用法眨猎,了解了箭頭函數(shù)的用法之后呢抑进,我們一起來進(jìn)一步的看一看箭頭函數(shù),到底和我們的普通函數(shù)有什么區(qū)別睡陪,好處在哪寺渗?

1.沒有this

箭頭函數(shù)沒有this,所以需要通過查找作用域鏈來確定this的值兰迫。
這就意味著如果箭頭函數(shù)被非箭頭函數(shù)包含信殊,this綁定的就是最近一層非箭頭函數(shù)的this。
模擬一個(gè)實(shí)際開發(fā)中的例子:
我們的需求是點(diǎn)擊一個(gè)按鈕汁果,改變?cè)摪粹o的背景色鸡号。
為了方便開發(fā),我們抽離一個(gè)Button組件须鼎,當(dāng)需要使用的時(shí)候直接:傳入元素id值即可綁定該元素點(diǎn)擊時(shí)改變背景色的事件

new Button('button');
// HTML代碼如下:
<button id="button">點(diǎn)擊變色</button>
// javascript代碼:
function Button(id) {
  this.element = document.querySelector('#' + id);
  this.bindEvent();
}

Button.prototype.bindEvent = function() {
  this.element.addEventListener("click", this.setBgColor, false);
}

Button.prototype.setBgColor = function() {
  this.element.style.backgroundColor = '#abcdef';
}

vat button = new Button('button');

看著好像沒有問題鲸伴,結(jié)果卻是報(bào)錯(cuò) Uncaught TypeError: Cannot read property 'style' of undefined
這是因?yàn)楫?dāng)使用addEventListener() 為一個(gè)元素注冊(cè)事件的時(shí)候,事件函數(shù)里的this值是該元素的引用晋控。
所以如果我們?cè)趕etBgColor中console.log(this), this指向的是按鈕元素汞窗,那this.element就是undefined, 報(bào)錯(cuò)自然就理所當(dāng)然了赡译。
也許你會(huì)問仲吏,既然this都指向了按鈕元素,那我們直接修改setBgColor函數(shù)為:

Button.prototype.setBgColor = function() {
  this.style.backgroundColor = '#abcdef';
}

不就可以解決這個(gè)問題了蝌焚?
確實(shí)可以這樣做裹唆,但是在實(shí)際開發(fā)中,我們可能會(huì)在setBgColor中還調(diào)用其他的函數(shù)只洒,比如寫成這樣:

 Button.prototype.setBgColor = function() {
  this.setElementColor();
  this.setOtherElementColor();
}

所以我們還是希望setBgColor中的this是指向?qū)嵗龑?duì)象的许帐,這樣就可以調(diào)用其他的函數(shù)。
利用ES5毕谴,我們一般會(huì)這樣做:

Button.prototype.bindEvent = function() {
  this.element.addEventListener("click", this.setBgColor.bind(this), false);
}

為避免addEventListener的影響成畦,使用bind強(qiáng)制綁定setBgColor()的this為實(shí)例對(duì)象
使用ES6,我們可以更好的解決這個(gè)問題:

Button.prototype.bindEvent = function() {
  this.element.addEventListener("click", event => this.setBgColor(event), false);
}

由于箭頭函數(shù)沒有this涝开,所以會(huì)向外層查找this的值循帐,即bindEvent中的this,此時(shí)this指向?qū)嵗龑?duì)象舀武,所以可以正確的調(diào)用this.setBgColor方法拄养,而this.setBgColor中的this也會(huì)正確指向?qū)嵗龑?duì)象。
在這里再額外提一點(diǎn)银舱,就是注意bindEvent和setBgColor在這里使用的是普通函數(shù)的形式瘪匿,而非箭頭函數(shù)跛梗,如果我們改成箭頭函數(shù),會(huì)導(dǎo)致函數(shù)里的this指向window對(duì)象(非嚴(yán)格模式下)柿顶。
最后,因?yàn)榧^函數(shù)沒有this操软,所以也不能用call()嘁锯、apply()、bind()這些方法改變this的指向聂薪,可以看一個(gè)例子:

var value = 1;
var result = (() => this.value).bind({value: 2})();
console.log(result); // 1
2.沒有arguments

箭頭函數(shù)沒有自己的arguments對(duì)象家乘,這不一定是件壞事,因?yàn)榧^函數(shù)可以訪問外圍函數(shù)的arguments對(duì)象:

 function constant() {
  return () => arguments[0];
}

var result = constant(1);
console.log(result()); // 1

那如果我們就是要訪問箭頭函數(shù)的參數(shù)呢藏澳?
你可以通過命名參數(shù)或者rest參數(shù)的形式訪問參數(shù):

let nums = (...nums) => nums;
3.不能通過new關(guān)鍵字調(diào)用

Javascript函數(shù)有兩個(gè)內(nèi)部方法:[[Call]]和[[Construct]]仁锯。
當(dāng)通過new調(diào)用函數(shù)時(shí),執(zhí)行[[Construct]]方法翔悠,創(chuàng)建一個(gè)實(shí)例對(duì)象业崖,然后再執(zhí)行函數(shù)體,將this綁定到實(shí)例上蓄愁。
當(dāng)直接調(diào)用的時(shí)候双炕,執(zhí)行[[Call]]方法,直接執(zhí)行函數(shù)體撮抓。
箭頭函數(shù)并沒有[[Construct]]方法妇斤,不能被用作構(gòu)造函數(shù),如果通過new的方式調(diào)用丹拯,會(huì)報(bào)錯(cuò)站超。

var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
4.沒有原型

由于不能使用new調(diào)用箭頭函數(shù),所以也沒有構(gòu)建原型的需求乖酬,于是箭頭函數(shù)也不存在prototype這個(gè)屬性死相。

5.沒有super

連原型都沒有,自然也不能通過super來訪問原型的屬性咬像,所以箭頭函數(shù)也沒有super的媳纬,不過跟this、arguments一樣施掏,這些值由外圍最近一層非箭頭函數(shù)決定钮惠。
在出現(xiàn)了箭頭函數(shù)之后是不是就可以舍棄原來的普通函數(shù)了呢?答案一定是否定的七芭,一些情況下我們最好不要去進(jìn)行箭頭函數(shù)的操作素挽,那都有哪些情況呢?

(1) 在對(duì)象上定義函數(shù)

先來看下面這段代碼

var obj = {
  array: [1, 2, 3],
  sum: () => {
    console.log(this === window); // => true
    return this.array.reduce((result, item) => result + item);
  }
}
obj.sum();
// Throws "TypeError: Cannot read property 'reduce' of undefined"

sum方法定義在obj對(duì)象上狸驳,當(dāng)調(diào)用的時(shí)候我們發(fā)現(xiàn)拋出了一個(gè)TypeError预明,因?yàn)楹瘮?shù)中的this是window對(duì)象缩赛,所以this.array也是undefined。原因也很簡(jiǎn)單撰糠,相信只要了解es6 箭頭函數(shù)的都知道
箭頭函數(shù)沒有它自己的this值酥馍,箭頭函數(shù)內(nèi)的this值繼承自外圍作用域
解決方法也很簡(jiǎn)單,就是不用唄阅酪。這里可以用es6里函數(shù)表達(dá)式的簡(jiǎn)潔語法旨袒,在這種情況下,this值就取決于函數(shù)的調(diào)用方式了术辐。

var obj = {
  array: [1, 2, 3],
  sum() {
    console.log(this === obj); // => true
    return this.array.reduce((result, item) => result + item);
  }
}
obj.sum(); // => 6

通過object.method()語法調(diào)用的方法使用非箭頭函數(shù)定義砚尽,這些函數(shù)需要從調(diào)用者的作用域中獲取一個(gè)有意思的this值。

(2)在原型上定義函數(shù)

在對(duì)象原型上定義函數(shù)也是遵循著一樣的規(guī)則

function Person(pName) {
  this.pName = pName;
}
Person.prototype.sayName = () => {
  console.log(this === window); // => true
  return this.pName;
}
var person = new Person('zhangSan');
person.sayName(); // => undefined

// 使用function函數(shù)表達(dá)式
function Person(pName) {
  this.pName = pName;
}
Person.prototype.sayName = function() {
  console.log(this === person); // => true
  return this.pName;
}
var person = new Person('zhangSan');
person.sayName(); // => zhangSan
(3)動(dòng)態(tài)上下文中的回調(diào)函數(shù)

this是js中非常強(qiáng)大的特點(diǎn)辉词,他讓函數(shù)可以根據(jù)其調(diào)用方式動(dòng)態(tài)的改變上下文必孤,然后箭頭函數(shù)直接在聲明時(shí)就綁定了this對(duì)象,所以不再是動(dòng)態(tài)的瑞躺。
在客戶端敷搪,在dom元素上綁定事件監(jiān)聽函數(shù)是非常普遍的行為,在dom事件被觸發(fā)時(shí)幢哨,回調(diào)函數(shù)中的this指向該dom购啄,可當(dāng)我們使用箭頭函數(shù)時(shí):

var button = document.getElementById('myButton');
button.addEventListener('click', () => {
  console.log(this === window); // => true
  this.innerHTML = 'clicked button';
})

因?yàn)檫@個(gè)回調(diào)的箭頭函數(shù)是在全局上下文中被定義的,所以他的this是window嘱么。所以當(dāng)this是由目標(biāo)對(duì)象決定時(shí)狮含,我們應(yīng)該使用函數(shù)表達(dá)式:

var button = document.getElementById('myButton');
button.addEventListener('click', function() {
  console.log(this === button); // => true
  this.innerHTML = 'clicked button';
})
(4)構(gòu)造函數(shù)中

在構(gòu)造函數(shù)中,this指向新創(chuàng)建的對(duì)象實(shí)例
this instanceOf MyFunction === true
需要注意的是曼振,構(gòu)造函數(shù)不能使用箭頭函數(shù)几迄,如果這樣做會(huì)拋出異常

var Person = (name) => {
  this.name = name;
}
var person = new Person('zhangSan');
// Uncaught TypeError: Person is not a constructor

理論上來說也是不能這么做的,因?yàn)榧^函數(shù)在創(chuàng)建時(shí)this對(duì)象就綁定了冰评,更不會(huì)指向?qū)ο髮?shí)例映胁。

(5)太簡(jiǎn)短的函數(shù)

箭頭函數(shù)可以讓語句寫的非常的簡(jiǎn)潔,但是一個(gè)真實(shí)的項(xiàng)目甲雅,一般由多個(gè)開發(fā)者共同協(xié)作完成解孙,就算由單人完成,后期也并不一定是同一個(gè)人維護(hù)抛人,箭頭函數(shù)有時(shí)候并不會(huì)讓人很好的理解弛姜,比如

let multiply = (a, b) => b === undefined ? b => a * b : a * b;
let double = multiply(2);
double(3); // => 6
multiply(2,3); // => 6

這個(gè)函數(shù)的作用就是當(dāng)只有一個(gè)參數(shù)a時(shí),返回接受一個(gè)參數(shù)b返回a * b的函數(shù)妖枚,接受兩個(gè)參數(shù)時(shí)直接返回乘積廷臼,這個(gè)函數(shù)可以很好的工作并且看起來很簡(jiǎn)潔,但是從第一眼看去并不是很好理解。為了讓這個(gè)函數(shù)更好的讓人理解荠商,我們可以為這個(gè)箭頭函數(shù)加一對(duì)花括號(hào)寂恬,并加上return語句買或者直接使用函數(shù)表達(dá)式:

function multiply(a, b) {
  if (b === undefined) {
    return function (b) {
      return a * b;
    }
  }
  return a * b;
}
let double = multiplu(2);
double(3); // => 6;
multiply(2, 3); // => 6

總結(jié)
最后,關(guān)于箭頭函數(shù)莱没,引用MDN的介紹就是:箭頭函數(shù)表達(dá)式的語法比函數(shù)表達(dá)式更短初肉,并且不綁定自己的this,arguments饰躲,super牙咏。
毫無疑問,箭頭函數(shù)帶來了很多便利属铁。恰當(dāng)?shù)氖褂眉^函數(shù)可以讓我們避免使用早起的.bind()函數(shù)或者需要固定上下文的地方并且讓代碼更加簡(jiǎn)潔眠寿。
箭頭函數(shù)也有一些不便利的地方躬翁。我們?cè)谛枰獎(jiǎng)討B(tài)上下文的地方不能使用箭頭函數(shù):定義需要?jiǎng)討B(tài)上下文的函數(shù)焦蘑,構(gòu)造函數(shù),需要this對(duì)象作為目標(biāo)的回調(diào)函數(shù)以及用箭頭函數(shù)難以理解的語句盒发。在其他情況下例嘱,請(qǐng)盡情使用箭頭函數(shù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末宁舰,一起剝皮案震驚了整個(gè)濱河市拼卵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛮艰,老刑警劉巖腋腮,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異壤蚜,居然都是意外死亡即寡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門袜刷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來聪富,“玉大人,你說我怎么就攤上這事著蟹《章” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵萧豆,是天一觀的道長(zhǎng)奸披。 經(jīng)常有香客問我,道長(zhǎng)涮雷,這世上最難降的妖魔是什么源内? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上膜钓,老公的妹妹穿的比我還像新娘嗽交。我一直安慰自己,他們只是感情好颂斜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布夫壁。 她就那樣靜靜地躺著,像睡著了一般沃疮。 火紅的嫁衣襯著肌膚如雪盒让。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天司蔬,我揣著相機(jī)與錄音邑茄,去河邊找鬼。 笑死俊啼,一個(gè)胖子當(dāng)著我的面吹牛肺缕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播授帕,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼同木,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了跛十?” 一聲冷哼從身側(cè)響起彤路,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芥映,沒想到半個(gè)月后洲尊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奈偏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年坞嘀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霎苗。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡姆吭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唁盏,到底是詐尸還是另有隱情内狸,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布厘擂,位于F島的核電站昆淡,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏刽严。R本人自食惡果不足惜昂灵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧眨补,春花似錦管削、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至甘晤,卻和暖如春含潘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背线婚。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工遏弱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人塞弊。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓漱逸,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親居砖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子虹脯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345