原文:dmitripavlutin.com/when-not-to…javascript
譯者:前端小智html
阿里雲最近在作活動,低至2折,有興趣能夠看看:promotion.aliyun.com/ntms/yunpar…前端
爲了保證的可讀性,本文采用意譯而非直譯。java
這些年來,ES6 將 JS 的可用性提高到一個新的水平時: 箭頭函數、類等等,這些都很棒。git
箭頭函數是最有價值的新功能之一,有不少好文章描述了它的上下文透明性和簡短的語法。github
但每一個事務都有兩面。一般,新特性會帶來一些混亂,其中之一就是箭頭函數被誤導了。本文將介紹一些場景,在這些場景中,你應該繞過箭頭函數,轉而使用良好的舊函數表達式或較新的簡寫語法。而且要注意縮短代碼,由於這會影響代碼的可讀性。編程
在JS中,方法是存儲在對象屬性中的函數。當調用該方法時,this
將指向該方法所屬的對象。數組
因爲箭頭函數語法簡短,因此使用它來定義方法是頗有吸引力的,讓我們來試一試:瀏覽器
const calculate = {
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"
calculate.sum();
複製代碼
calculate.sum
方法用箭頭函數定義。 可是在調用時,calculate.sum()
會拋出一個TypeError
,由於this.array
爲undefined
。閉包
當調用calculate
對象上的方法sum()
時,上下文仍然是 window
。之因此會發生這種狀況,是由於箭頭函數按詞法做用域將上下文綁定到 window
對象。
執行this.array
等同於window.array
,它是undefined
。
解決方法是使用常規函數表達式來定義方法。 this 是在調用時肯定的,而不是由封閉的上下文決定的,來看看修復後的版本:
const calculate = {
array: [1, 2, 3],
sum() {
console.log(this === calculate); // => true
return this.array.reduce((result, item) => result + item);
}
};
calculate.sum(); // => 6
複製代碼
由於sum
是常規函數,因此在調用 calculate.sum()
時 this
是 calculate
對象。 this.array
是數組引用,所以正確計算元素之和:6
。
一樣的規則也適用於在原型對象上定義方法。使用一個箭頭函數來定義sayCatName
方法,this
指向 window
function MyCat(name) {
this.catName = name;
}
MyCat.prototype.sayCatName = () => {
console.log(this === window); // => true
return this.catName;
};
const cat = new MyCat('Mew');
cat.sayCatName(); // => undefined
複製代碼
使用早期的方式定義函數表達式:
function MyCat(name) {
this.catName = name;
}
MyCat.prototype.sayCatName = function() {
console.log(this === cat); // => true
return this.catName;
};
const cat = new MyCat('Mew');
cat.sayCatName(); // => 'Mew'
複製代碼
sayCatName
常規函數在做爲方法調用時將上下文更改成cat
對象:cat.sayCatName()
。
this
在JS中是一個強大的特性,它容許根據調用函數的方式更改上下文。一般,上下文是調用發生的目標對象,這使得代碼更加天然,就像這個對象發生了什麼。
可是,箭頭函數會在聲明上靜態綁定上下文,而且沒法使其動態化,但這種方式有壞也有好,有時候咱們須要動態綁定。
在客戶端編程中,將事件偵聽器附加到DOM元素是一項常見的任務。事件觸發處理程序函數,並將this
做爲目標元素,這裏若是使用箭頭函數就不夠靈活。
下面的示例嘗試爲這樣的處理程序使用箭頭函數:
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this === window); // => true
this.innerHTML = 'Clicked button';
});
複製代碼
在全局上下文中 this
指向 window
。 當發生單擊事件時,瀏覽器嘗試使用按鈕上下文調用處理函數,但箭頭函數不會更改其預約義的上下文。this.innerHTML
至關於window.innerHTML
,沒有任何意義。
必須應用函數表達式,該表達式容許根據目標元素更改 this
:
const button = document.getElementById('myButton');
button.addEventListener('click', function() {
console.log(this === button); // => true
this.innerHTML = 'Clicked button';
});
複製代碼
當用戶單擊按鈕時,處理程序函數中的 this
指向 button
。所以這個問題。innerHTML = 'Clicked button'
正確地修改按鈕文本以反映已單擊狀態。
this
在構造調用中是新建立的對象。當執行new MyFunction()
時,構造函數MyFunction
的上下文是一個新對象:this instanceof MyFunction === true
。
注意,箭頭函數不能用做構造函數。 JavaScript經過拋出異常隱式阻止這樣作。
不管如何,this
是來自封閉上下文的設置,而不是新建立的對象。換句話說,箭頭函數構造函數調用沒有意義,並且是模糊的。
讓咱們看看若是嘗試這樣作會發生什麼:
const Message = (text) => {
this.text = text;
};
// Throws "TypeError: Message is not a constructor"
const helloMessage = new Message('Hello World!');
複製代碼
執行new Message('Hello World!')
,其中Message
是一個箭頭函數,JavaScript拋出一個 TypeError
錯誤,Message
不能用做構造函數。
上面的例子可使用函數表達式來修復,這是建立構造函數的正確方法(包括函數聲明):
const Message = function(text) {
this.text = text;
};
const helloMessage = new Message('Hello World!');
複製代碼
箭頭函數有一個很好的屬性,它能夠省略參數圓括號()
、塊大括號{}
,若是函數主體只有一條語句,則返回。這有助於編寫很是短的函數。
原文做者的大學編程教授給學生一個有趣的任務:編寫 用C語言計算字符串長度的最短函數,這是學習和探索新語言的好方式。
然而,在實際應用程序中,許多開發人員都會閱讀代碼。 最短的語法並不老是適合幫助你的同事即時瞭解該方法的用途。
在某種程度上,簡寫的函數變得難以閱讀,因此儘可能不要過分使用。讓各位們看一個例子
const multiply = (a, b) => b === undefined ? b => a * b : a * b;
const double = multiply(2);
double(3); // => 6
multiply(2, 3); // => 6
複製代碼
multiply
返回兩個數字的乘法結果或與第一個參數綁定的閉包,以便之後的乘法運算。
該函數運行良好,看起來很短。但從一開始就很難理解它是作什麼的。
爲了使其更具可讀性,能夠從箭頭函數恢復可選花括號和return
語句,或使用常規函數:
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
複製代碼
在簡短和冗長之間找到一個平衡點是很好的,這樣可使代碼更加直觀。
毫無疑問,箭頭函數是一個很好的補充。當正確使用時,它會使前面必須使用.bind()
或試圖捕獲上下文的地方變得簡單,它還簡化了代碼。
某些狀況下的優勢會給其餘狀況帶來不利。 當須要動態上下文時,不能使用箭頭函數:定義方法,使用構造函數建立對象,在處理事件時從 this
獲取目標。
代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug。
爲了回饋讀者,《大遷世界》不按期舉行(每月一到三次),現金抽獎活動,保底200,外加用戶讚揚,但願你能成爲大遷世界的小錦鯉,快來試試吧
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。
附:新文章會提早一天發到公衆號