Blog
Blog copied to clipboard
[MDN] 箭頭函式運算式
Arrow Function Expressions
-
箭頭函式運算式 比傳統的
函式運算式寫法更簡潔, 但是他有限制, 無法運用在所有情況. -
不同之處 & 限制:
- 沒有自己的
this或super, 所以不應該用於methods. - 沒有
arguments或new.target的關鍵字. - 不適合使用
call,apply和bind的 methods, 因為這些方法通常依賴於所建立的scope. - 無法用於
contructors - 主體內無法使用
yield
- 沒有自己的
比較傳統函式與箭頭函式
// 傳統函式
function (a) {
return a + 100;
}
// 拆解成箭頭函式
// 1. 移除單字 "function" 並在參數和函式主體的開頭大括號之間放置箭頭
(a) => {
return a + 100;
}
// 2. 移除函式主體的大括號, 以及 "return".
(a) => a + 100;
// 3. 移除參數的小括號
a => a + 100;
大括號, 小括號 和 "return" 有時是選填, 有時是必填.
- 舉例, 如果有 多個參數 或 沒有參數, 需要在參數周圍重新引進小括號.
// 傳統函式(沒有參數)
let a = 4;
let b = 2;
function () {
return a + b + 100;
}
// 箭頭函式(沒有參數)
let a = 4;
let b = 2;
() => a + b + 100;
- 如果函式主體需要 額外幾行 的處理過程, 需要重新引進大括號 外加 "return" (箭頭函式不會去猜你要 "回傳" 甚麼, 或何時要 "回傳"):
// 傳統函式
function (a, b) {
let chuck = 42;
return a + b + chuck;
}
// 箭頭函式
(a, b) => {
let chuck = 42;
return a + b + chuck;
}
- 最後, 將 具名函式 的箭頭運算式視為變數
// 傳統函式
function bob(a) {
return a + 100;
}
// 箭頭函式
let bob = (a) => a + 100;
語法
基本語法
- 只有一個參數, 不需要 "return":
param => expression
- 多個參數, 不需要 "return":
(param1, paramN) => expression
- 多行 statements, 需要主體大括號, 以及 "return":
param => {
let a = 1;
return a + param;
};
- 多個參數. 多行 statements 需要主體大括號, 以及 "return":
(param1, paramN) => {
let a = 1;
return a + param1 + paramN;
}
進階語法
- 回傳一個 object literal 運算式需要在周圍加上小括號:
params => ({foo: "a"}) // 回傳物件 {foo: "a"}
- 支援 rest 參數:
(a, b, ...r) => expression
- 支援預設參數:
(a=400, b=20, c) => expression
- 支援參數間的 destructing:
([a, b] = [10, 20]) => a + b; // 結果是 30
({ a, b } = { a: 10, b: 20 }) => a + b; // 結果是 30
說明
用於 methods
"use strict";
var obj = {
// 不會建立一個新的 scope
i: 10,
b: () => console.log(this.i, this),
c: function () {
console.log(this.i, this);
},
};
obj.b(); // undefined, Window {...} (或是全域物件)
obj.c(); // 10, Object {...}
- 箭頭函式不會有自己的
this.
"use strict";
var obj = {
a: 10,
};
Object.defineProperty(obj, "b", {
get: () => {
console.log(this.a, typeof this.a, this);
// undefined 'undefined' Window {...} (或是全域物件)
return this.a + 10;
// this 代表全域物件 'Window', 所以 'this.a' 回傳 'undefined'
},
});
call, apply 和 bind
-
call,apply和bind方法 不適合 使用 - 因為 箭頭函式所建立的 "this" 是根據所定義的 scope 而定.
// ----------------------
// 傳統範例
// ----------------------
// 一個單純的物件, 有自己的 "this".
var obj = {
num: 100,
};
// 在 window 上設定 "num"
window.num = 2020;
// 一個單純的傳統函式運用了 "this"
var add = function (a, b, c) {
return this.num + a + b + c;
};
// call
var result = add.call(obj, 1, 2, 3); // 建立了 "obj" scope
console.log(result); // 106
// apply
const arr = [1, 2, 3];
var result = add.apply(obj, arr); // 建立了 "obj" scope
console.log(result); // 106
// bind
var result = add.bind(obj); // 建立了 "obj" scope
console.log(result(1, 2, 3)); // 106
// ----------------------
// 箭頭範例
// ----------------------
// 一個單純的物件, 有自己的 "this".
var obj = {
num: 100,
};
// 在 window 上設定 "num"
window.num = 2020;
// 箭頭函式
var add = (a, b, c) => this.num + a + b + c;
// call
console.log(add.call(obj, 1, 2, 3)); // 結果是 2026
// apply
const arr = [1, 2, 3];
console.log(add.apply(obj, arr)); // 結果是 2026
// bind
const bound = add.bind(obj);
console.log(bound(1, 2, 3)); // 結果是 2026
傳統範例:
var obj = {
count: 10,
doSomethingLater: function () {
setTimeout(function () {
// 在 window scope 執行函式
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater(); // "NaN", 因為 "count" 不在 window scope.
箭頭範例:
var obj = {
count: 10,
doSomethingLater: function () {
setTimeout(() => {
this.count++;
console.log(this.count);
}, 300);
},
};
obj.doSomethingLater();
沒有 arguments
- 不會有自己的
arguments物件. 因此,arguments是一個參考, 指向 enclosing scope arguments
var arguments = [1, 2, 3];
var arr = () => arguments[0];
arr(); // 1
function foo(n) {
var f = () => arguments[0] + n; // arguments[0] is n
return f();
}
foo(3); // 3 + 3 = 6
function foo(n) {
var f = (...args) => args[0] + n;
return f(10);
}
foo(1); // 11
new 運算子
- 無法用於建構子, 使用
new會拋出錯誤.
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo 不是一個建構子
prototype 屬性
- 不會有
prototype屬性.
var Foo = () => {};
console.log(Foo.prototype); // undefined
yield 關鍵字
- 在主體中無法使用
yield關鍵字. 因此無法用於 generators.
函式主體
- 可以是 "簡潔主體" 或 "區塊主體".
- 簡潔主體, 只會有一個運算式, 並運算成回傳值.
- 區塊主體, 必須表明
return.
var func = (x) => x * x;
var func = (x, y) => {
return x + y;
};
回傳 object literal
var func = () => { foo: 1 };
// 呼叫 func() 回傳 undefined!
var func = () => { foo: function() {} };
// SyntaxError: 函式敘述句需要一個名稱
- 因為在大括號裡的程式碼會被解析為一連串的 statements, 必須用小括號包住:
var func = () => ({ foo: 1 });
換行
- 無法在參數和箭頭間換行.
var func = (a, b, c)
=> 1;
// SyntaxError: 預期是運算式, 但得到 '=>'
- 不過可以經過修正, 透過在箭頭後面換行, 或使用小/大括號, 確保程式碼保持整齊. 也可以在參數間換行.
var func = (a, b, c) =>
1;
var func = (a, b, c) => (
1
);
var func = (a, b, c) => {
return 1
};
var func = (
a,
b,
c
) => 1;
// 沒有拋出 SyntaxError
解析順序
- 雖然箭頭(
=>)不是一個運算子, 但他有特殊的解析規則, 與常規函式的operator precedence不同.
let callback;
callback = callback || function() {}; // ok
callback = callback || () => {};
// SyntaxError: 不合法的箭頭函式參數
callback = callback || (() => {}); // ok
範例
基本用途
let empty = () => {};
// undefined
(() => "foobar")();
// "foobar"
// (這是一個立即呼叫的函式運算式)
var simple = (a) => (a > 15 ? 15 : a);
simple(16); // 15
simple(10); // 10
let max = (a, b) => (a > b ? a : b);
// 陣列篩選和映射 ...
var arr = [5, 6, 13, 0, 1, 18, 23];
var sum = arr.reduce((a, b) => a + b);
// 66
var even = arr.filter((v) => v % 2 == 0);
// [6, 0, 18]
var double = arr.map((v) => v * 2);
// [10, 12, 26, 0, 2, 36, 46]
// promise chains
promise
.then((a) => {
// ...
})
.then((b) => {
// ...
});
// 無參數
setTimeout(() => {
console.log("我不久後發生");
setTimeout(() => {
// 更深的程式
console.log("我之後發生");
}, 1);
}, 1);
瀏覽器相容性
- 只有 IE 不支援, 其他瀏覽器都完全支援