'ฟังก์ชันลูกศร' และ 'ฟังก์ชัน' เทียบเท่า / ใช้แทนกันได้หรือไม่?
ฟังก์ชัน Arrow ใน ES2015 ให้ไวยากรณ์ที่กระชับยิ่งขึ้น
- ฉันสามารถแทนที่การประกาศ / นิพจน์ฟังก์ชันทั้งหมดของฉันด้วยฟังก์ชันลูกศรได้หรือไม่
- ฉันต้องระวังอะไรบ้าง?
ตัวอย่าง:
ฟังก์ชันตัวสร้าง
function User(name) {
this.name = name;
}
// vs
const User = name => {
this.name = name;
};
วิธีการต้นแบบ
User.prototype.getName = function() {
return this.name;
};
// vs
User.prototype.getName = () => this.name;
วิธีวัตถุ (ตามตัวอักษร)
const obj = {
getName: function() {
// ...
}
};
// vs
const obj = {
getName: () => {
// ...
}
};
การโทรกลับ
setTimeout(function() {
// ...
}, 500);
// vs
setTimeout(() => {
// ...
}, 500);
ฟังก์ชัน Variadic
function sum() {
let args = [].slice.call(arguments);
// ...
}
// vs
const sum = (...args) => {
// ...
};
คำตอบ
tl; dr: ไม่! ฟังก์ชันลูกศรและการประกาศฟังก์ชัน / นิพจน์ไม่เทียบเท่าและไม่สามารถแทนที่แบบสุ่มสี่สุ่มห้าได้
หากฟังก์ชั่นที่คุณต้องการที่จะเปลี่ยนไม่ได้ใช้this
, arguments
และไม่ได้เรียกว่ามีnew
แล้วใช่
จึงมักจะเป็น: มันขึ้นอยู่กับ ฟังก์ชันลูกศรมีพฤติกรรมที่แตกต่างจากการประกาศฟังก์ชัน / นิพจน์ดังนั้นเรามาดูความแตกต่างกันก่อน:
1. คำศัพท์this
และarguments
ฟังก์ชันลูกศรไม่มีของตัวเองthis
หรือการarguments
ผูก แต่ตัวระบุเหล่านั้นจะได้รับการแก้ไขในขอบเขตคำศัพท์เช่นเดียวกับตัวแปรอื่น ๆ นั่นหมายความว่าภายในฟังก์ชันลูกศรthis
และarguments
อ้างถึงค่าของthis
และarguments
ในสภาพแวดล้อมฟังก์ชันลูกศรถูกกำหนดไว้ใน (เช่นฟังก์ชันลูกศร "นอก"):
// Example using a function expression
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: function() {
console.log('Inside `bar`:', this.foo);
},
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObject
// Example using a arrow function
function createObject() {
console.log('Inside `createObject`:', this.foo);
return {
foo: 42,
bar: () => console.log('Inside `bar`:', this.foo),
};
}
createObject.call({foo: 21}).bar(); // override `this` inside createObject
ในกรณีการแสดงออกของฟังก์ชันthis
หมายถึงวัตถุที่สร้างขึ้นภายในไฟล์createObject
. ฟังก์ชั่นในกรณีที่ลูกศรthis
หมายถึงthis
ของcreateObject
ตัวเอง
สิ่งนี้ทำให้ฟังก์ชันลูกศรมีประโยชน์หากคุณต้องการเข้าถึงthis
สภาพแวดล้อมปัจจุบัน:
// currently common pattern
var that = this;
getData(function(data) {
that.data = data;
});
// better alternative with arrow functions
getData(data => {
this.data = data;
});
หมายเหตุว่าเรื่องนี้ก็หมายความว่าเป็นไม่ได้ที่เป็นไปได้ในการตั้งค่าฟังก์ชั่นของลูกศรthis
ด้วยหรือ.bind
.call
หากคุณไม่ค่อยคุ้นเคยthis
ให้ลองอ่าน
2. ไม่สามารถเรียกใช้ฟังก์ชันลูกศรด้วย new
ES2015 แยกความแตกต่างระหว่างฟังก์ชันที่สามารถเรียกและฟังก์ชันที่สามารถสร้างได้ ถ้าฟังก์ชันสามารถสร้างได้ก็สามารถเรียกด้วย new
เช่นnew User()
. ถ้าฟังก์ชันสามารถเรียกใช้ได้ก็สามารถเรียกได้โดยไม่ต้องnew
(เช่นการเรียกฟังก์ชันปกติ)
ฟังก์ชันที่สร้างขึ้นผ่านการประกาศฟังก์ชัน / นิพจน์มีทั้งที่สร้างได้และสามารถเรียกใช้ได้
ฟังก์ชันลูกศร (และวิธีการ) สามารถเรียกใช้ได้เท่านั้น
class
ตัวสร้างสามารถสร้างได้เท่านั้น
หากคุณพยายามเรียกใช้ฟังก์ชันที่เรียกไม่ได้หรือสร้างฟังก์ชันที่ไม่สามารถสร้างได้คุณจะได้รับข้อผิดพลาดรันไทม์
เมื่อทราบสิ่งนี้เราสามารถระบุสิ่งต่อไปนี้ได้
เปลี่ยนได้:
- ฟังก์ชันที่ไม่ใช้
this
หรือarguments
. - ฟังก์ชันที่ใช้กับ
.bind(this)
ไม่สามารถเปลี่ยนได้:
- ฟังก์ชันตัวสร้าง
- เพิ่มฟังก์ชัน / วิธีการในต้นแบบ (เพราะมักจะใช้
this
) - ฟังก์ชัน Variadic (หากใช้
arguments
(ดูด้านล่าง))
มาดูสิ่งนี้โดยใช้ตัวอย่างของคุณ:
ฟังก์ชันตัวสร้าง
new
นี้จะไม่ทำงานเพราะฟังก์ชั่นลูกศรไม่สามารถเรียกว่ามี ใช้การประกาศฟังก์ชัน / นิพจน์หรือใช้class
ต่อไป
วิธีการต้นแบบ
ไม่น่าจะเป็นไปได้มากนักเนื่องจากวิธีการต้นแบบมักจะใช้this
เพื่อเข้าถึงอินสแตนซ์ หากไม่ได้ใช้this
คุณสามารถเปลี่ยนได้ อย่างไรก็ตามหากคุณสนใจไวยากรณ์ที่กระชับเป็นหลักให้ใช้class
ไวยากรณ์วิธีการที่กระชับ:
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
วิธีการของวัตถุ
ในทำนองเดียวกันสำหรับวิธีการในตัวอักษรวัตถุ หากเมธอดต้องการอ้างอิงอ็อบเจ็กต์เองthis
โดยใช้นิพจน์ฟังก์ชันต่อไปหรือใช้ไวยากรณ์วิธีการใหม่:
const obj = {
getName() {
// ...
},
};
การโทรกลับ
มันขึ้นอยู่กับ. คุณควรแทนที่อย่างแน่นอนหากคุณใช้นามแฝงด้านนอกthis
หรือใช้.bind(this)
:
// old
setTimeout(function() {
// ...
}.bind(this), 500);
// new
setTimeout(() => {
// ...
}, 500);
แต่:ถ้ารหัสที่เรียกการโทรกลับตั้งค่าอย่างชัดเจนthis
เป็นค่าเฉพาะเช่นเดียวกับที่มักเกิดขึ้นกับตัวจัดการเหตุการณ์โดยเฉพาะกับ jQuery และการเรียกกลับใช้this
(หรือarguments
) คุณจะไม่สามารถใช้ฟังก์ชันลูกศรได้
ฟังก์ชัน Variadic
เนื่องจากฟังก์ชันลูกศรไม่มีเป็นของตัวเองarguments
คุณจึงไม่สามารถแทนที่ด้วยฟังก์ชันลูกศรได้ อย่างไรก็ตาม ES2015 แนะนำทางเลือกในการใช้arguments
คือพารามิเตอร์ที่เหลือ
// old
function sum() {
let args = [].slice.call(arguments);
// ...
}
// new
const sum = (...args) => {
// ...
};
คำถามที่เกี่ยวข้อง:
- ฉันควรใช้ฟังก์ชัน Arrow ใน ECMAScript 6 เมื่อใด
- ฟังก์ชันลูกศร ES6 มีอาร์กิวเมนต์ของตัวเองหรือไม่?
- อะไรคือความแตกต่าง (ถ้ามี) ระหว่างฟังก์ชันลูกศร ES6 และฟังก์ชันที่ผูกกับ Function.prototype.bind
- วิธีใช้ฟังก์ชันลูกศร (ฟิลด์คลาสสาธารณะ) เป็นวิธีการเรียน
แหล่งข้อมูลเพิ่มเติม:
ฟังก์ชั่นลูกศร => คุณสมบัติ ES6 ที่ดีที่สุดจนถึงตอนนี้ พวกเขาเป็นส่วนเสริมที่ทรงพลังอย่างมากสำหรับ ES6 ที่ฉันใช้อยู่ตลอดเวลา
เดี๋ยวก่อนคุณไม่สามารถใช้ฟังก์ชั่นลูกศรได้ทุกที่ในโค้ดของคุณมันจะไม่ทำงานในทุกกรณีเช่นthis
เมื่อฟังก์ชันลูกศรไม่สามารถใช้งานได้ ไม่ต้องสงสัยเลยว่าฟังก์ชันลูกศรเป็นส่วนเสริมที่ยอดเยี่ยมซึ่งทำให้โค้ดมีความเรียบง่าย
แต่คุณไม่สามารถใช้ฟังก์ชันลูกศรเมื่อจำเป็นต้องใช้บริบทแบบไดนามิก: การกำหนดวิธีการสร้างวัตถุด้วยตัวสร้างรับเป้าหมายจากสิ่งนี้เมื่อจัดการกับเหตุการณ์
ไม่ควรใช้ฟังก์ชันลูกศรเนื่องจาก:
พวกเขาไม่มี
this
โดยใช้ "การกำหนดขอบเขตศัพท์" เพื่อหาว่าค่าของ "
this
" ควรเป็นเท่าใด ในคำง่ายๆการกำหนดขอบเขตคำศัพท์จะใช้ "this
" จากภายในเนื้อความของฟังก์ชันพวกเขาไม่มี
arguments
ฟังก์ชันลูกศรไม่มี
arguments
วัตถุ แต่ฟังก์ชันเดียวกันสามารถทำได้โดยใช้พารามิเตอร์ส่วนที่เหลือlet sum = (...args) => args.reduce((x, y) => x + y, 0)
sum(3, 3, 1) // output - 7
`ไม่สามารถใช้กับไฟล์
new
ฟังก์ชั่นลูกศรไม่สามารถเป็นตัวกำหนดได้เนื่องจากไม่มีคุณสมบัติต้นแบบ
เมื่อใดควรใช้ฟังก์ชั่นลูกศรและเมื่อไม่:
- อย่าใช้เพื่อเพิ่มฟังก์ชันเป็นคุณสมบัติในตัวอักษรของวัตถุเพราะเราไม่สามารถเข้าถึงสิ่งนี้ได้
- นิพจน์ฟังก์ชันเหมาะที่สุดสำหรับเมธอดอ็อบเจ็กต์ ลูกศรฟังก์ชั่นที่ดีที่สุดสำหรับการเรียกกลับหรือวิธีการเช่น
map
, หรือreduce
forEach
- ใช้การประกาศฟังก์ชันสำหรับฟังก์ชันที่คุณเรียกด้วยชื่อ (เนื่องจากถูกยกขึ้น)
- ใช้ฟังก์ชันลูกศรสำหรับการโทรกลับ (เนื่องจากมีแนวโน้มที่จะสั้นกว่า)
ในการใช้ฟังก์ชันลูกศรfunction.prototype.call
ฉันได้สร้างฟังก์ชันตัวช่วยในต้นแบบวัตถุ:
// Using
// @func = function() {use this here} or This => {use This here}
using(func) {
return func.call(this, this);
}
การใช้งาน
var obj = {f:3, a:2}
.using(This => This.f + This.a) // 5
แก้ไข
คุณไม่จำเป็นต้องมีผู้ช่วย คุณสามารถทำได้:
var obj = {f:3, a:2}
(This => This.f + This.a).call(undefined, obj); // 5