ฟังก์ชันลูกศรและคีย์เวิร์ด `this` ใน JavaScript

การปรับปรุงครั้งล่าสุด: 02/10/2026
  • ฟังก์ชันลูกศรมีไวยากรณ์ที่กระชับและสามารถจับภาพได้ this โดยอาศัยบริบททางคำศัพท์จากขอบเขตโดยรอบ แทนที่จะสร้างการเชื่อมโยงของตนเอง
  • คุณค่าของ this การทำงานของฟังก์ชันทั่วไปขึ้นอยู่กับวิธีการเรียกใช้ ซึ่งส่งผลต่อฟังก์ชัน เมธอด คอนสตรัคเตอร์ คลาส และฟังก์ชันเรียกกลับ (callback)
  • ฟังก์ชันลูกศรเหมาะสำหรับฟังก์ชันเรียกกลับ (callback) และเมธอดของอาร์เรย์ แต่ไม่เหมาะสำหรับเมธอดของอ็อบเจ็กต์ ตัวจัดการเหตุการณ์ DOM และคอนสตรัคเตอร์
  • เข้าใจเมื่อไร this การแยกแยะระหว่างฟังก์ชันแบบไดนามิกและแบบเชิงคำศัพท์นั้นมีความสำคัญอย่างยิ่งในการหลีกเลี่ยงข้อผิดพลาดเล็กน้อย และในการเลือกใช้ระหว่างฟังก์ชันลูกศรและฟังก์ชันแบบดั้งเดิม

ฟังก์ชันลูกศรและสิ่งนี้ใน JavaScript

หากคุณเคยเข้าสู่ระบบมาก่อน this คุณไม่ใช่คนเดียวที่ใช้ฟังก์ชัน JavaScript ต่างกันแล้วได้ผลลัพธ์ที่แตกต่างกันอย่างมาก นักพัฒนาหลายคนพบเจอกรณีที่เมธอดพิมพ์อ็อบเจ็กต์ที่คาดหวังออกมา แต่ฟังก์ชันลูกศรพิมพ์ออกมาเป็นอย่างอื่น windowและลูกศรที่ซ้อนกันก็ชี้กลับไปยังวัตถุโดยรอบอย่าง "น่าอัศจรรย์" การเข้าใจว่าทำไมจึงเกิดเหตุการณ์เช่นนั้นจึงเป็นกุญแจสำคัญในการเขียนโค้ดที่คาดเดาได้และปราศจากข้อผิดพลาด

ฟังก์ชันลูกศรและ this คีย์เวิร์ด form เป็นหนึ่งในชุดคีย์เวิร์ดที่สำคัญที่สุด (และมักถูกเข้าใจผิด) ใน JavaScript ยุคใหม่ ฟังก์ชันลูกศรดูเหมือนจะเป็นเพียงไวยากรณ์ที่สั้นกว่า แต่ในทางเทคนิคแล้วมันเปลี่ยนวิธีการทำงานของฟังก์ชัน this เราจะมาเรียนรู้วิธีการทำงานของ Callback และแม้กระทั่งว่าเมื่อไหร่ควรหรือไม่ควรใช้ Callback เป็นเมธอด เราจะมาเรียนรู้ทุกอย่างทีละขั้นตอน ตั้งแต่ไวยากรณ์ไปจนถึงบริบทการทำงาน โดยใช้ภาษาที่เข้าใจง่ายและตัวอย่างมากมาย

ไวยากรณ์ฟังก์ชันลูกศรโดยไม่ทำให้สับสน

ฟังก์ชันลูกศรคือ นิพจน์ฟังก์ชันที่เขียนด้วย => ไวยากรณ์แทนที่จะเป็น function คำสำคัญ. ในเชิงแนวคิด คุณสามารถมองว่ามันเป็นวิธีการเขียนที่กระชับกว่า นั่นคือ “รับพารามิเตอร์เหล่านี้ ประเมินนิพจน์หรือบล็อกโค้ดนี้ และส่งคืนค่า” โดยพื้นฐานแล้ว มันก็ยังคงเป็นฟังก์ชันอยู่ แต่พวกมันทำงานแตกต่างกันในหลายแง่มุมที่สำคัญ

ฟังก์ชันลูกศรพื้นฐานที่สุดจะแปลงเป็นนิพจน์ฟังก์ชันปกติโดยตรง ตัวอย่างเช่น นิพจน์ฟังก์ชันแบบคลาสสิกนี้:

const multiplyByTwo = function (value) { return value * 2; };

สามารถเขียนใหม่เป็นฟังก์ชันลูกศรได้ดังนี้:

const multiplyByTwo = (value) => { return value * 2; };

ฟังก์ชันลูกศรจะแสดงประสิทธิภาพได้ดีที่สุดเมื่อเนื้อหาเป็นเพียงนิพจน์เดียว ถ้าเนื้อหาเป็นเพียงคำสั่งเดียวที่ส่งค่ากลับมา คุณสามารถละเว้นทั้งวงเล็บปีกกาและคำสั่งที่ระบุอย่างชัดเจนได้ returnซึ่งช่วยให้สามารถส่งคืนค่าโดยปริยายได้:

const multiplyByTwo = value => value * 2;

เมื่อมีพารามิเตอร์เพียงตัวเดียว คุณสามารถละวงเล็บที่ล้อมรอบได้ แต่เฉพาะในกรณีนั้นเท่านั้น So x => x * 2 ถูกต้อง แต่ถ้าคุณมีพารามิเตอร์ศูนย์ตัวหรือหลายตัว คุณต้องใส่เครื่องหมายวงเล็บไว้ด้วย:

  • พารามิเตอร์ศูนย์: () => 42
  • พารามิเตอร์หนึ่งตัว: x => x * 2 or (x) => x * 2
  • พารามิเตอร์สองตัวขึ้นไป: (x, y) => x + y

เมื่อคุณต้องการคำสั่งมากกว่าหนึ่งคำสั่งในเนื้อหา คุณต้องใช้เครื่องหมายวงเล็บปีกกาและระบุอย่างชัดเจน return. ในสถานการณ์นั้น ฟังก์ชันลูกศรจะทำงานเหมือนฟังก์ชันทั่วไปในส่วนของการส่งคืนค่า: ไม่มี returnไม่มีค่าใด ๆ ส่งกลับมา

const feedCat = (status) => {
if (status === 'hungry') {
return 'Feed the cat';
} else {
return 'Do not feed the cat';
}
};

โปรดระมัดระวังเมื่อส่งคืนค่าอ็อบเจ็กต์จากฟังก์ชันลูกศร เนื่องจากวงเล็บปีกกาของอ็อบเจ็กต์อาจทำให้สับสนกับส่วนเนื้อหาของฟังก์ชันได้ เพื่อหลีกเลี่ยงความกำกวมนั้น ให้ใส่เครื่องหมายวงเล็บครอบออบเจ็กต์ลิเทอรัลไว้ เพื่อให้ JavaScript รู้ว่าเป็นนิพจน์ที่จะส่งคืน:

const toObject = value => ({ result: value });

อีกประเด็นหนึ่ง: ฟังก์ชันลูกศรเป็นนิพจน์เสมอ ไม่ใช่การประกาศตัวแปร นั่นหมายความว่าค่าเหล่านั้นจะต้องถูกกำหนดให้กับตัวแปร คุณสมบัติ หรือส่งเป็นอาร์กิวเมนต์ ไม่สามารถอยู่โดดๆ ได้เหมือนค่าอื่นๆ function myFunc() {}และค่าเหล่านั้นจะไม่ถูกยกขึ้นในลักษณะเดียวกับการประกาศฟังก์ชัน ดังนั้นคุณจึงไม่สามารถเรียกใช้ค่าเหล่านั้นได้ก่อนที่จะมีการกำหนดนิยาม

สิ่งที่แน่นอนคือ this ใน JavaScript?

คีย์เวิร์ด this เป็นการเชื่อมโยงแบบไดนามิกที่ JavaScript สร้างขึ้นให้คุณเมื่อเรียกใช้ฟังก์ชันหรือเมธอดของคลาส คุณอาจมองว่ามันเป็นพารามิเตอร์ที่มองไม่เห็น ซึ่งค่าของมันขึ้นอยู่กับวิธีการและตำแหน่งที่เรียกใช้ฟังก์ชัน นี่ทำให้มันมีประสิทธิภาพและยืดหยุ่น แต่ก็เป็นแหล่งที่มาของความสับสนอย่างมากเช่นกัน

ในฟังก์ชันที่ไม่เข้มงวด this ผลลัพธ์ที่ได้จะเป็นอ็อบเจ็กต์บางประเภทเสมอ ในโหมดเข้มงวด ผลลัพธ์ที่ได้อาจเป็นค่าใดก็ได้ รวมถึงค่าอื่นๆ ด้วย undefined. JavaScript จะกำหนดค่าดังกล่าวโดยพิจารณาจากบริบทการทำงาน: ฟังก์ชันปกติ, การเรียกเมธอด, การเรียกคอนสตรัคเตอร์, คลาส, ขอบเขตส่วนกลาง หรือฟังก์ชันลูกศร

ในระดับสูงสุดของสคริปต์แบบคลาสสิก (ไม่ใช่โมดูล) this อ้างถึง globalThisซึ่งโดยปกติจะเป็นของเบราว์เซอร์ window วัตถุ. ดังนั้น การเปรียบเทียบต่อไปนี้ในเบราว์เซอร์จึงเป็นจริง:

console.log(this === window); // true

ในฟังก์ชันที่ไม่ใช่ลูกศร this ขึ้นอยู่กับสถานที่โทรโดยสิ้นเชิง ถ้าคุณโทร obj.method()จากนั้นข้างใน method ค่าของ this is objถ้าคุณนำฟังก์ชันเดียวกันนั้นมาเรียกใช้แบบแยกต่างหาก fn() ในโหมดเข้มงวด this จะกลายเป็น undefinedในโหมดที่ไม่เข้มงวด JavaScript จะ "แทนที่" this สีสดสวย globalThis.

สิ่งสำคัญไม่ได้อยู่ที่ว่าฟังก์ชันนั้นถูกนิยามไว้ที่ใด แต่ขึ้นอยู่กับวิธีการเรียกใช้ฟังก์ชันนั้นต่างหาก เมธอดสามารถคงอยู่บนสายโซ่ต้นแบบหรือถูกกำหนดใหม่ให้กับวัตถุอื่นได้โดยยังคงมองเห็นได้ this ขึ้นอยู่กับวัตถุที่ใช้จริงในขณะเรียกใช้ การส่งต่อเมธอดไปมามักจะเปลี่ยนแปลงคุณสมบัติของมัน this เว้นแต่คุณจะแก้ไขมันอย่างชัดเจน

นอกจากนี้ยังมีเครื่องมือสำหรับควบคุมอีกด้วย this อย่างชัดเจน: call, apply, bindและ Reflect.apply. สิ่งเหล่านี้ช่วยให้คุณ "ฉีด" สิ่งที่ต้องการเข้าไปได้ this ราคา: fn.call(obj, arg1, arg2) จะดำเนินการ fn สีสดสวย this ตั้งค่าให้ objกฎการแทนที่เดียวกันนี้ใช้ได้ในโหมดที่ไม่เข้มงวด: หากคุณผ่าน null or undefined as thisพวกมันจะถูกแทนที่ด้วย globalThis; ค่าพื้นฐานจะถูกบรรจุลงในอ็อบเจ็กต์ห่อหุ้มของมัน

ฟังก์ชัน Callback เพิ่มความซับซ้อนอีกชั้นหนึ่ง เนื่องจาก this การควบคุมขึ้นอยู่กับว่าใครโทรเข้ามาที่หมายเลขเรียกกลับของคุณ วิธีการวนซ้ำอาร์เรย์ Promise ตัวสร้าง และ API ที่คล้ายกันมักจะเรียกใช้ฟังก์ชันเรียกกลับด้วย this ตั้งค่าให้ undefined (หรืออ็อบเจ็กต์ส่วนกลางในโหมดไม่เรียบร้อย) API บางตัว เช่น Array.prototype.forEach or Set.prototype.forEachยอมรับแยกต่างหาก thisArg พารามิเตอร์ที่คุณสามารถใช้เพื่อตั้งค่าฟังก์ชันเรียกกลับ (callback) this.

API อื่นๆ จงใจเรียกใช้ฟังก์ชันเรียกกลับ (callback) ด้วยฟังก์ชันที่กำหนดเอง this ค่า ยกตัวอย่างเช่น reviver อาร์กิวเมนต์ถึง JSON.parse และ replacer สำหรับ JSON.stringify รับ this ตัวจัดการเหตุการณ์ใน DOM จะผูกอยู่กับวัตถุที่เป็นเจ้าของคุณสมบัติที่กำลังประมวลผลอยู่ เมื่อเขียนในรูปแบบ "คลาสสิก"

แนวคิดหลัก: ฟังก์ชันลูกศรไม่ได้สร้างฟังก์ชันของตัวเอง this

คุณลักษณะเด่นของฟังก์ชันลูกศรคือ มันจะไม่สร้างอ็อบเจ็กต์ใหม่เลย this ผูกพัน. แต่พวกมันจะปิดทับ (หรือ "ยึดครอง") this โดยอิงจากสภาพแวดล้อมทางคำศัพท์โดยรอบในขณะที่สร้างขึ้น เมื่อลูกศรทำงานในภายหลัง มันจะนำค่าที่ถูกจับไว้มาใช้ซ้ำโดยไม่คำนึงถึงวิธีการเรียกใช้

ในทางปฏิบัติ ฟังก์ชันลูกศรจะทำงานราวกับว่ามันถูกผูกไว้กับตัวแปรโดยอัตโนมัติอย่างถาวร this จากขอบเขตภายนอกของมัน นี่คือเหตุผลว่าทำไมวิธีการต่างๆ เช่น call, applyและ bind ไม่สามารถเปลี่ยนแปลง this สำหรับฟังก์ชันลูกศร: thisArg ข้อโต้แย้งนั้นจะถูกเพิกเฉยไปโดยสิ้นเชิง คุณยังสามารถส่งพารามิเตอร์ปกติผ่านพวกมันได้ แต่... this ค่าถูกล็อกไว้แล้ว

ลองพิจารณาส่วนโค้ดต่อไปนี้ในขอบเขตโดยรวมของไฟล์สคริปต์:

const arrow = () => console.log(this);
arrow();

เนื่องจากลูกศรถูกกำหนดไว้ในโค้ดส่วนกลาง ดังนั้นมันจึง... this คือโลก this (โดยทั่วไป window ในสคริปต์ของเบราว์เซอร์) และสิ่งนั้นจะไม่เปลี่ยนแปลง การเรียกร้อง arrow ในฐานะฟังก์ชันธรรมดา การกำหนดค่าให้กับคุณสมบัติ หรือการส่งต่อฟังก์ชันนั้น จะทำให้ได้ค่าของอ็อบเจ็กต์ส่วนกลางเดียวกันเสมอเมื่อถูกเรียกใช้ในบริบทนี้

พฤติกรรมที่น่าสนใจอย่างแท้จริงจะปรากฏขึ้นเมื่อคุณใช้ฟังก์ชันลูกศรซ้อนอยู่ภายในฟังก์ชันหรือเมธอดทั่วไป เนื่องจากลูกศรจับฟังก์ชันภายนอกไว้ thisจึงกลายเป็นเครื่องมือที่มีประสิทธิภาพสำหรับฟังก์ชันเรียกกลับ (callback) ที่จำเป็นต้องอ้างอิงกลับไปยังอ็อบเจ็กต์ที่บรรจุอยู่ โดยไม่ต้องใช้วิธีปกติ .bind(this) พิธี.

const counter = {
id: 42,
start() {
setTimeout(() => {
console.log(this.id); // uses counter.id
}, 1000);
},
};

If start กำลังใช้ฟังก์ชันนิรนามแบบดั้งเดิมอยู่ภายใน setTimeoutคุณจะต้องทำการผูกข้อมูลด้วยตนเอง this หรือบันทึกไว้ในตัวแปร ด้วยลูกศร ฟังก์ชันเรียกกลับจะสืบทอดคุณสมบัติโดยธรรมชาติ this จาก startซึ่งเป็น counterดังนั้น this.id พิมพ์ 42 ตามที่ตั้งใจไว้.

การเชื่อมโยงทางคำศัพท์นี้ยังอธิบายถึงคำถามคลาสสิกที่ว่า “ทำไมถึงเป็นเช่นนั้น” ได้อีกด้วย this คำถาม "เปลี่ยน" เมื่อใช้ลูกศรในออบเจ็กต์ลิเทอรัล ลองดูวัตถุสองชิ้นนี้:

const obj1 = {
speak() {
console.log(this);
}
};

const obj2 = {
speak: () => {
console.log(this);
}
};

การเรียกร้อง obj1.speak() พิมพ์ obj1, เพราะ speak เป็นวิธีการปกติและ this ตั้งค่าตามสถานที่โทร ตรงกันข้าม, obj2.speak() บันทึกภายนอก this (บ่อยครั้ง window ในเบราว์เซอร์) เนื่องจากลูกศรไม่ได้ใช้วัตถุนั้นเป็นตัวของมัน thisตัวอักขระของวัตถุเองไม่ได้สร้างวัตถุใหม่ this ขอบเขต; มีเพียงส่วนเนื้อหาของฟังก์ชันเท่านั้นที่ทำเช่นนั้น และฟังก์ชันลูกศรจะข้ามขั้นตอนนั้นไป

ทีนี้ลองพิจารณาเมธอดของอ็อบเจ็กต์ที่สร้างและเรียกลูกศรภายในทันที:

const obj3 = {
speak() {
(() => {
console.log(this);
})();
}
};

obj3.speak();

ในสถานการณ์นี้ ฟังก์ชันลูกศรภายในจะสืบทอดคุณสมบัติ this จาก speakซึ่งเป็น obj3 เมื่อเรียกว่าเป็น obj3.speak(). แม้ว่าลูกศรจะเป็นฟังก์ชันซ้อนที่ถูกเรียกใช้งานทันที แต่ก็ยังชี้ไปยัง obj3ไม่ใช่สิ่งที่เป็นสากล นั่นคือสาระสำคัญของศัพท์เฉพาะ this: มันจะติดตามขอบเขตโดยรอบ ไม่ใช่จุดที่ลูกศรชี้ไปโดยตรง

this ครอบคลุมทั้งฟังก์ชัน อ็อบเจ็กต์ และคอนสตรัคเตอร์

เพื่อที่จะเชี่ยวชาญฟังก์ชันลูกศรอย่างแท้จริงและ thisมันช่วยให้เข้าใจได้ดีขึ้นว่าอย่างไร this ใช้งานได้ในบริบทหลักทุกรูปแบบ: ฟังก์ชันทั่วไป เมธอด คอนสตรัคเตอร์ คลาส และขอบเขตทั่วโลก เมื่อเข้าใจกฎเหล่านั้นแล้ว พฤติกรรมของลูกศรก็จะเข้าใจได้ง่ายขึ้นมาก

ในฟังก์ชันธรรมดา (ไม่ใช่ฟังก์ชันลูกศร) this ขึ้นอยู่กับวิธีการเรียกใช้ฟังก์ชันโดยสิ้นเชิง ถ้าคุณโทร fn() ในโหมดเข้มงวด this is undefinedในโหมดที่ไม่เรียบร้อย การแทนที่ทำให้ this กลายเป็น globalThis. ถ้าคุณโทร obj.fn()แล้ว this is obj. เคลื่อนไหว fn ไปยังวัตถุอื่นหรือไปยังตัวแปรและค่าของ this จะดำเนินการตามนั้น

ในเมธอดที่กำหนดไว้บนอ็อบเจ็กต์ลิเทอรัล this คือวัตถุที่เรียกใช้เมธอด ไม่จำเป็นต้องเป็นวัตถุที่เมธอดนั้นถูกกำหนดไว้ตั้งแต่แรก If obj.__proto__ มีเมธอดอยู่ และคุณเรียกใช้เมธอดนั้น obj.method()จากนั้นข้างใน method, this is objไม่ใช่ต้นแบบ

คอนสตรัคเตอร์เป็นกรณีพิเศษอีกอย่างหนึ่ง: เมื่อคุณเรียกฟังก์ชันด้วย new, this ผูกติดอยู่กับอินสแตนซ์ของวัตถุที่สร้างขึ้นใหม่ ตัวอย่างเช่นใน function User(name) { this.name = name; }, โทร new User('Alex') ชุด this ไปที่ใหม่ User วัตถุ หากตัวสร้างส่งคืนวัตถุที่ไม่ใช่ประเภทพื้นฐานโดยชัดเจน วัตถุที่ส่งคืนนั้นจะแทนที่วัตถุเดิม this เนื่องจากค่าสุดท้ายของ new การแสดงออก

ไวยากรณ์ของคลาสสร้างขึ้นจากกฎเหล่านี้ โดยมีบริบทหลักสองประการ ได้แก่ อินสแตนซ์และสแตติก ภายในคอนสตรัคเตอร์หรือเมธอดอินสแตนซ์ this ชี้ไปยังอินสแตนซ์ของคลาสที่คุณกำลังใช้งาน ภายในเมธอดแบบสแตติกหรือบล็อกการเริ่มต้นแบบสแตติก this หมายถึงคลาสเอง (หรือคลาสที่สืบทอดมาเมื่อเรียกใช้ผ่านการสืบทอด) ฟิลด์อินสแตนซ์จะถูกประเมินด้วย this ผูกกับอินสแตนซ์ใหม่; ดูฟิลด์คงที่ this ในฐานะตัวสร้างคลาส

ตัวสร้างคลาสที่สืบทอดมาจะมีพฤติกรรมแตกต่างออกไปเล็กน้อย จนกว่าคุณจะเรียกใช้ super()ไม่มีสิ่งใดที่ใช้งานได้ this. กล่าวอ้าง super() เริ่มต้น this โดยการมอบหมายงานให้กับคอนสตรัคเตอร์พื้นฐาน การส่งคืนค่าก่อนที่จะทำเช่นนั้นในคอนสตรัคเตอร์ที่สืบทอดมานั้น อนุญาตเฉพาะในกรณีที่คุณส่งคืนอ็อบเจ็กต์ที่แตกต่างกันอย่างชัดเจนเท่านั้น

ในบริบทระดับโลก this ขึ้นอยู่กับว่าสภาพแวดล้อม JavaScript ห่อหุ้มและประมวลผลโค้ดของคุณอย่างไร ในสคริปต์เบราว์เซอร์แบบคลาสสิก ระดับบนสุด this คืออ็อบเจ็กต์ส่วนกลาง ในโมดูล ES จะเป็นอ็อบเจ็กต์ระดับบนสุด this อยู่เสมอ undefinedโมดูล CommonJS ของ Node.js นั้นถูกห่อหุ้มไว้ภายในและโดยปกติจะทำงานด้วย this ตั้งค่าให้ module.exportsแอตทริบิวต์ตัวจัดการเหตุการณ์แบบอินไลน์ใน HTML จะทำงานเมื่อ this กำหนดให้ตรงกับองค์ประกอบที่มันเชื่อมต่ออยู่

รายละเอียดเล็กน้อยแต่สำคัญอย่างหนึ่งคือ ตัวอักษรแสดงวัตถุเองไม่ได้สร้างสิ่งใหม่ขึ้นมา this ขอบเขต. การเขียน const obj = { value: this }; ภายในสคริปต์จะทำให้ obj.value เท่ากับด้านนอก thisไม่ใช่ตัววัตถุ เฉพาะส่วนของฟังก์ชัน (และส่วนของคลาส) เท่านั้นที่สร้างวัตถุเฉพาะขึ้นมา this การผูกข้อมูล; ลูกศรจงใจข้ามขั้นตอนนี้และสืบทอดคุณสมบัติ

เหตุใดฟังก์ชันลูกศรจึงเหมาะสำหรับใช้เป็นฟังก์ชันเรียกกลับ (และเมื่อใดที่ไม่เหมาะสม)

เนื่องจากฟังก์ชันลูกศรปิดทับ thisพวกมันเหมาะอย่างยิ่งสำหรับสถานการณ์การเรียกกลับหลายๆ แบบ ที่คุณต้องการให้การเรียกกลับนั้นอ้างอิงถึงวัตถุหรือบริบทโดยรอบอยู่เสมอ วิธีนี้มีประโยชน์อย่างยิ่งเมื่อใช้ร่วมกับตัวจับเวลา คำสัญญา และเมธอดของอาร์เรย์ เช่น map, filterและ reduce.

ลองนึกภาพวิธีการที่จำเป็นต้องอัปเดตคุณสมบัติบางอย่างซ้ำๆ โดยใช้ setInterval. โดยใช้ฟังก์ชันแบบดั้งเดิม this ภายในฟังก์ชันเรียกกลับ (callback) จะใช้ค่าเริ่มต้นเป็นอ็อบเจ็กต์ส่วนกลาง (หรือ) undefined ในโหมดเข้มงวด) ดังนั้น this.count จะไม่ชี้ไปยังอินสแตนซ์ของคุณ เมื่อใช้ฟังก์ชันลูกศร ฟังก์ชันเรียกกลับจะใช้โดยธรรมชาติ this ของวิธีการภายนอก

function Counter() {
this.count = 0;

setInterval(() => {
this.count++;
}, 1000);
}

ขอบคุณลูกศรนั้น this ภายในฟังก์ชันเรียกกลับช่วงเวลาจะอ้างอิงถึง Counter ตัวอย่างเช่น ไม่ใช่ window. ถ้าฟังก์ชันเรียกกลับนั้นเป็นฟังก์ชันปกติ คุณจะต้อง... .bind(this) หรือตัวแปรตัวกลางเช่น const self = this; เพื่อเก็บรักษาข้อมูลอ้างอิงไว้

ฟังก์ชันลูกศรยังช่วยลดความซับซ้อนของโค้ดเมื่อใช้วิธีการของอาร์เรย์ ซึ่งบ่อยครั้งคุณไม่จำเป็นต้องคำนึงถึงเรื่องนี้มากนัก this เลย เมื่อคุณส่งฟังก์ชันแบบดั้งเดิมเป็นฟังก์ชันเรียกกลับ (callback) โดยปริยาย this มักจะ undefinedและคุณอาจลืมเรื่องนั้นไป ลูกศรทำให้เห็นได้อย่างชัดเจนว่าฟังก์ชันนั้นเป็นเพียงการแมปอินพุตไปยังเอาต์พุตเท่านั้น

const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);

อย่างไรก็ตาม มีกรณีสำคัญบางกรณีที่ฟังก์ชันลูกศรไม่ใช่ตัวเลือกที่เหมาะสม โดยเฉพาะอย่างยิ่งเมื่อคุณต้องการฟังก์ชันแบบไดนามิก this. สองรูปแบบการเขียนโค้ดที่ไม่ดี (anti-pattern) ที่พบได้บ่อยคือ การใช้ฟังก์ชันลูกศรเป็นเมธอดของอ็อบเจ็กต์ และเป็นตัวจัดการเหตุการณ์ DOM ที่อาศัย... this เป็นองค์ประกอบนั้น

ลองพิจารณาวัตถุที่ใช้ติดตามชีวิตของแมวตัวหนึ่ง:

const cat = {
lives: 9,
jump: () => {
this.lives--; // bug: this is not cat
},
};

cat.jump();

ตั้งแต่ jump เป็นลูกศร this ไม่ได้หมายถึง cat แต่ไม่ว่าจะอย่างไรก็ตาม this คือตำแหน่งที่สร้างอ็อบเจ็กต์ลิเทอรัล (ซึ่งมักจะเป็นอ็อบเจ็กต์ส่วนกลาง) ที่ตั้งใจไว้ this.lives-- ไม่ว่าจะโยนข้อยกเว้น (ในโหมดเข้มงวด) หรือเปลี่ยนแปลงสิ่งที่ไม่เกี่ยวข้องโดยไม่แจ้งให้ทราบ การใช้ไวยากรณ์เมธอดแบบปกติในกรณีนี้จึงเป็นวิธีที่ถูกต้อง

ตัวรับฟังเหตุการณ์ DOM ก็คล้ายกัน: รูปแบบมาตรฐาน this.classList.toggle('on') ภายในฟังก์ชันเรียกกลับของเหตุการณ์นั้นขึ้นอยู่กับ this เป็นองค์ประกอบที่จุดชนวนเหตุการณ์นั้น โดยใช้ฟังก์ชันลูกศร this เนื่องจากไม่ได้ชี้ไปยังองค์ประกอบนั้นอีกต่อไป โค้ดจึงทำงานผิดพลาด

const button = document.getElementById('press');

button.addEventListener('click', () => {
this.classList.toggle('on'); // this is not button
});

ในสถานการณ์นี้ ตัวจัดการควรเป็นฟังก์ชันปกติเพื่อให้ this ถูกผูกไว้โดยเบราว์เซอร์กับองค์ประกอบปุ่ม ฟังก์ชันลูกศรไม่สามารถใช้แทนกันได้โดยตรง หากตรรกะของคุณคาดหวัง... this เพื่อเป็นเป้าหมายของเหตุการณ์แบบไดนามิก

ข้อเสียอีกประการหนึ่งที่สังเกตได้ยากคือ ฟังก์ชันลูกศรนั้นไม่มีชื่อเรียกทางไวยากรณ์ โดยปกติแล้วตัวแปรเหล่านี้จะไม่มีชื่อเฉพาะ (นอกเหนือจากตัวแปรที่กำหนดให้) ซึ่งอาจทำให้การติดตามการทำงานของโปรแกรม (stack trace) มีรายละเอียดน้อยลง และการเรียกซ้ำ (recursion) ซับซ้อนขึ้นเล็กน้อย ในโค้ดที่ใช้งานจริงส่วนใหญ่ นี่เป็นข้อแลกเปลี่ยนที่จัดการได้ แต่ก็เป็นสิ่งที่ควรจดจำไว้

กรณีพิเศษ: เมธอด getter, setter, เมธอดที่ถูกผูกไว้ และกรณีแปลกๆ

ฟังก์ชันรับค่าและฟังก์ชันกำหนดค่าใช้กฎ "ตำแหน่งการเรียกใช้" เดียวกัน: this คือวัตถุที่เข้าถึงคุณสมบัตินั้น ไม่ใช่วัตถุที่กำหนดคุณสมบัตินั้นไว้ตั้งแต่แรก หากเมธอด getter ถูกสืบทอดมาจาก prototype และคุณเรียกใช้เมธอดนั้นกับอ็อบเจ็กต์ที่สืบทอดมา this ภายในเมธอด getter จะอ้างอิงถึงอ็อบเจ็กต์ที่สืบทอดมา

เมธอดที่ผูกไว้ซึ่งสร้างขึ้นด้วย Function.prototype.bind ให้พฤติกรรมที่คล้ายกับฟังก์ชันลูกศร แต่ในระดับของฟังก์ชันปกติ เมื่อคุณโทร f.bind(obj)คุณสร้างฟังก์ชันใหม่ซึ่ง... this ติดตั้งอย่างถาวรกับ objไม่ว่าจะเรียกใช้ด้วยวิธีใดก็ตาม สิ่งนี้มีประโยชน์ในชั้นเรียนเมื่อคุณต้องการเก็บรักษาข้อมูลไว้ this แม้ว่าวิธีการนั้นจะแยกออกจากกันก็ตาม

class Example {
constructor() {
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
console.log(this); // always the instance
}
}

ข้อเสียของการใช้เมธอดแบบผูกติดและฟังก์ชันลูกศรเป็นฟิลด์ของอินสแตนซ์คือ แต่ละอินสแตนซ์จะได้รับสำเนาของฟังก์ชันนั้นเอง ซึ่งอาจทำให้การใช้หน่วยความจำเพิ่มขึ้น โดยทั่วไปแล้ว การแลกเปลี่ยนนี้เป็นที่ยอมรับได้เมื่อคุณผูกเมธอดที่ถูกเรียกใช้งานบ่อยๆ เพียงไม่กี่เมธอด แต่เป็นสิ่งที่ควรตระหนักถึงในโค้ดที่ต้องการประสิทธิภาพสูง

นอกจากนี้ยังมีกรณีพิเศษบางอย่างที่สืบทอดมาจากระบบเดิมอีกด้วย this มีพฤติกรรมที่แตกต่างกัน เช่น ภายในฟังก์ชันที่เลิกใช้งานแล้ว with คำแถลง ภายใน with (obj) { ... } บล็อกที่เรียกใช้ฟังก์ชันซึ่งเป็นคุณสมบัติของ obj มีผลเสมือนว่าคุณได้เขียนไว้ obj.method()ดังนั้น this ถูกผูกไว้กับ objโค้ดสมัยใหม่ควรหลีกเลี่ยง withแต่การเข้าใจข้อยกเว้นนี้จะช่วยให้เข้าใจได้ชัดเจนว่า this ยังคงขึ้นอยู่กับวิธีการเรียกใช้ฟังก์ชันเป็นหลัก

ตัวจัดการเหตุการณ์แบบอินไลน์ใน HTML ยังมีกฎพิเศษอีกอย่างหนึ่งคือ โค้ดส่วนรอบข้างของตัวจัดการเหตุการณ์แบบอินไลน์จะมองเห็น... this โดยถือเป็นองค์ประกอบ แต่ฟังก์ชันภายในที่กำหนดไว้ภายในตัวจัดการนั้นจะกลับไปใช้ฟังก์ชันปกติ this กฎระเบียบ ดังนั้น ฟังก์ชันดั้งเดิมภายในที่ไม่ผูกติดกับสิ่งใด มักจะมองเห็น... this as globalThis (หรือ undefined ในโหมดเข้มงวด) ไม่ใช่ตัวองค์ประกอบ

สุดท้ายนี้ โปรดจำไว้ว่าฟังก์ชันลูกศรไม่มี prototype คุณสมบัติและไม่สามารถใช้เป็นตัวสร้างได้ new. ความพยายาม new MyArrow() จะทำให้เกิดข้อผิดพลาด TypeError หากคุณต้องการฟังก์ชันที่สามารถทำหน้าที่เป็นตัวสร้าง คุณต้องใช้ฟังก์ชันปกติหรือคลาสแทน

การคำนึงถึงรายละเอียดเหล่านี้จะทำให้การเลือกระหว่างฟังก์ชันลูกศรและฟังก์ชันแบบดั้งเดิมง่ายขึ้นมาก ใช้ลูกศรในตำแหน่งที่คุณต้องการกำหนดคำศัพท์ this และมีไวยากรณ์ที่กระชับ และสามารถกลับไปใช้ฟังก์ชันปกติได้ทุกเมื่อที่ต้องการการทำงานแบบไดนามิกที่ขับเคลื่อนโดยตำแหน่งการเรียกใช้ this ความหมายของพฤติกรรมหรือตัวสร้าง

เมื่อคุณเข้าใจอย่างถ่องแท้แล้ว this เมื่อพิจารณาในทุกสถานการณ์ ฟังก์ชันลูกศรจะกลายเป็นพันธมิตรที่ทรงพลังแทนที่จะเป็นแหล่งที่มาของข้อผิดพลาดที่ไม่คาดคิด พวกเขาลดความซับซ้อนของรูปแบบทั่วไป เช่น การเรียกกลับและการแปลงข้อมูลอย่างง่าย ในขณะที่ฟังก์ชันปกติยังคงทำหน้าที่ที่ขึ้นอยู่กับฟังก์ชันของตนเอง this การผูกมัด เช่น เมธอด คอนสตรัคเตอร์ และตัวจัดการเหตุการณ์แบบไดนามิก

กระทู้ที่เกี่ยวข้อง: