隨着ES6標準的普及,JavaScript已經擁有許多新的語法糖,這讓我們編寫可讀的,高質量的代碼變得更加方便,但即使這樣仍然會遇到一些潛在的"陷阱"。
箭頭函數與對象字面量
箭頭函數提供了更簡潔和更短的語法,其中一個可用功能是您可以將函數編寫爲具有隱式返回值的lambda表達式。
例如:
const numbers = [1, 2, 3, 4];
numbers.map(function(n) {
return n * n;
});
可以使用箭頭函數簡化
const numbers = [1, 2, 3, 4];
numbers.map(n => n * n);
但如果我們希望映射到對象,可能並不會像我們期望的那樣。
const numbers = [1, 2, 3, 4];
numbers.map(n => { value: n });
上面的結果並非映射到對象,而是映射到undefined。花括號被解釋爲箭頭函數的塊範圍,值語句實際上最終成爲標籤,上面的代碼將被Javascript解釋器翻譯成:
const numbers = [1, 2, 3, 4];
numbers.map(function(n) {
value:
n
return;
});
解決方法非常微妙,我們只需要將對象包裝在括號中,將其轉換爲表達式而不是塊語句,如下所示:
const numbers = [1, 2, 3, 4];
numbers.map(n => ({ value: n }));
箭頭函數與this指針
箭頭函數沒有自己的this綁定,這意味着它們的this值將this與封閉的詞法範圍的值相同。儘管語法可以說是“更光滑”,但箭頭函數功能並不能適用於所有的情況,比如你可能會遇到:
let calculator = {
value: 0,
add: (values) => {
this.value = values.reduce((a, v) => a + v, this.value);
},
};
calculator.add([1, 2, 3]);
console.log(calculator.value); // 0
我們期望這裏的this
綁定的是計算器對象,但它實際上會導致this
未定義或指向全局對象。而常規函數是有一個this綁定,當在一個對象上調用時,它將指向該對象,因此在添加對象方法時仍然應該使用常規函數。
let calculator = {
value: 0,
add(values) {
this.value = values.reduce((a, v) => a + v, this.value);
},
};
calculator.add([10, 10]);
console.log(calculator.value); // 20
由於箭頭函數沒有this綁定,因此使用Function.prototype.call
,Function.prototype.bind
和Function.prototype.apply
也不能給它綁定上下文對象。
比如下面的代碼我們嘗試使用Function.prototype.call
爲箭頭函數綁定計算器對象,但實際上綁定的是全局對象。
const adder = {
add: (values) => {
this.value = values.reduce((a, v) => a + v, this.value);
},
};
let calculator = {
value: 0
};
adder.add.call(calculator, [1, 2, 3]);
console.log(calculator.value); // 0
自動插入分號
雖然自動插入分號(ASI)不是一個新功能,但它是JavaScript中最奇怪的功能之一,因此值得一提。
請看下面的代碼:
return
{
value: 42
}
有人可能認爲它將返回對象,但它實際上將返回undefined,因爲分號插入發生,使其成爲一個空的return語句,後跟一個block語句和一個label語句。
最終的代碼會被解釋成:
return;
{
value: 42
};
所以即使不得已使用分號,也不要在括號、模板字符串和普通字符串前使用換行。
淺集合
ES6中的集合不允許重複的元素,當元素是基本數據類型時,會比較值是否相等;當元素是引用數據類型時,會比較引用對象是否是同一個。
例如:
let set = new Set();
set.add([1, 2, 3]);
set.add([1, 2, 3]);
console.log(set.size); // 2
集合的最終長度是2,由於兩次添加的數組不是同一個。
let set = new Set();
set.add([1, 2, 3].join(','));
set.add([1, 2, 3].join(','));
console.log(set.size); // 1
最終會得到大小爲1的集合,因爲字符串是不可變。如果您發現自己需要存儲一組可以序列化的對象,則可以將其用作解決方法。
類和函數
在JavaScript中,常規函數被提升到詞法範圍的頂部,這意味着下面的示例將按照人們的預期運行:
let segment = new Segment();
function Segment() {
this.x = 0;
this.y = 0;
}
但是對於類來說情況並非如此,實際上沒有提升類,下面的代碼會導致異常Uncaught ReferenceError: Segment is not defined
let segment = new Segment();
class Segment {
constructor() {
this.x = 0;
this.y = 0;
}
}
Finally 關鍵字特例
請看下方的代碼:
(function(){
try {
return true;
} finally {
return false;
}
})() // false
我們可能認爲第一個return
語句使函數實際返回並彈出調用堆棧,但Finally
關鍵字例外,因爲finally語句總是運行所以結果是finally
塊中的return
語句返回。
PS:更多前端資訊、技術乾貨,請關注公衆號「前端新視界」,後臺回覆 “1” 獲取100本PDF前端電子書
回覆 “2” 獲取小編精選的Python編程電子書