คุณสามารถผูก 'this' ในฟังก์ชันลูกศรได้หรือไม่?


134

ฉันได้ทดลอง ES6 มาระยะหนึ่งแล้วและเพิ่งพบปัญหาเล็กน้อย

ฉันชอบใช้ฟังก์ชันลูกศรมากและเมื่อใดก็ตามที่ทำได้ฉันก็ใช้มัน

อย่างไรก็ตามดูเหมือนว่าคุณไม่สามารถผูกมันได้!

นี่คือฟังก์ชั่น:

var f = () => console.log(this);

นี่คือวัตถุที่ฉันต้องการผูกฟังก์ชันเข้ากับ:

var o = {'a': 42};

และนี่คือวิธีที่ฉันจะผูกfกับo:

var fBound = f.bind(o);

จากนั้นฉันสามารถโทรfBound:

fBound();

ซึ่งจะส่งออกสิ่งนี้ ( oวัตถุ):

{'a': 42}

เย็น! น่ารัก! ยกเว้นว่าใช้ไม่ได้. แทนที่จะส่งออกoอ็อบเจ็กต์มันจะส่งออกwindowอ็อบเจ็กต์

ฉันอยากรู้ว่า: คุณผูกฟังก์ชันลูกศรได้ไหม (และถ้าเป็นเช่นนั้นอย่างไร)


ฉันได้ทดสอบโค้ดด้านบนใน Google Chrome 48 และ Firefox 43 แล้วผลลัพธ์ก็เหมือนกัน


29
จุดรวมของฟังก์ชันลูกศรคือใช้thisขอบเขตหลัก
loganfsmyth

คำตอบ:


159

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

จากข้อมูลจำเพาะ ECMAScript 2015 :

การอ้างอิงถึงอาร์กิวเมนต์ super this หรือ new.target ภายใน ArrowFunction ต้องแก้ไขการผูกในสภาวะแวดล้อมที่ปิดด้วยคำศัพท์ โดยทั่วไปนี่จะเป็นสภาพแวดล้อมของฟังก์ชันของฟังก์ชันที่ปิดล้อมทันที


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

54

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

bind ยังคงมีค่าสำหรับอาร์กิวเมนต์ของฟังก์ชัน:

((a, b, c) => {
  console.info(a, b, c) // 1, 2, 3
}).bind(undefined, 1, 2, 3)()

ลองดูที่นี่: http://jsbin.com/motihanopi/edit?js,console


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

3
ใช้อาร์กิวเมนต์สำหรับบริบท: ((บริบท) => {someOtherFunction.apply (บริบท)}) ผูก (willBeIgnored บริบท) ()
cvazac

13

จากMDN :

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

ซึ่งหมายความว่าคุณไม่สามารถผูกค่าตามที่thisคุณต้องการได้


6

เป็นเวลาหลายปีที่นักพัฒนา js ต้องดิ้นรนกับการผูกบริบทถามว่าเหตุใดจึงthisมีการเปลี่ยนแปลงในจาวาสคริปต์ความสับสนอย่างมากในช่วงหลายปีที่ผ่านมาเนื่องจากการเชื่อมโยงบริบทและความแตกต่างระหว่างความหมายของthisในจาวาสคริปต์และthisในภาษา OOP อื่น ๆ ส่วนใหญ่

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

TL; DR

ไม่คุณไม่สามารถย้อนกลับฟังก์ชันลูกศรได้


7
ฟังก์ชัน Arrow ถูกสร้างขึ้นก่อนอื่นเพื่อให้ไวยากรณ์ที่สะอาดและเร็วขึ้น คิดแลมด้าแคลคูลัส น่าเสียดายที่ OO-zealots ที่รบกวนชีวิตของโปรแกรมเมอร์ JS ที่ใช้งานได้ฉวยโอกาสที่จะสละอิสระในการระบุบริบทการร้องขอ
Marco Faustinelli

5
การใช้thisไม่สามารถใช้งานได้ lambdas arguments => outputควรจะเป็น หากคุณต้องการบริบทภายนอกให้ส่งผ่านไปสิ่งที่มีอยู่thisคือสิ่งที่อำนวยความสะดวกในการใส่รูปแบบ OO ทั้งหมดลงในภาษา คุณจะไม่เคยได้ยินคำว่า "คลาสจาวาสคริปต์" หากไม่มีคำนี้
erich2k8

3
คุณสามารถย้อนกลับฟังก์ชันลูกศร thisเพียงแค่ไม่ได้
สติญน์เดอวิตต์

6

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

เราใช้การevalโทรที่นี่เพื่อสร้างฟังก์ชั่นลูกศรที่คุณส่งผ่านเป็นฟังก์ชันปกติจากนั้นใช้callเพื่อเรียกใช้ด้วยฟังก์ชันอื่นthis :

var obj = {value: 10};
function arrowBind(context, fn) {
  let arrowFn;
  (function() {
    arrowFn = eval(fn.toString());
    arrowFn();
  }).call(context);
}
arrowBind(obj, () => {console.log(this)});


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

4

ทำหน้าที่ ES6 Arrow แก้ปัญหา“ this” ใน JavaScript จริงๆ

ลิงก์ด้านบนอธิบายว่าฟังก์ชันลูกศรthisไม่เปลี่ยนแปลงด้วยbind, call, applyฟังก์ชัน

มีการอธิบายด้วยตัวอย่างที่ดีมาก

เรียกใช้สิ่งนี้node v4เพื่อดูลักษณะการทำงาน "ที่คาดไว้"

this.test = "attached to the module";
var foo = { test: "attached to an object" };
foo.method = function(name, cb){ 
    // bind the value of "this" on the method 
    // to try and force it to be what you want 
    this[name] = cb.bind(this); };
foo.method("bar", () => { console.log(this.test); });
foo.bar(); 

ขอให้คุณให้ข้อมูลที่เกี่ยวข้องเพิ่มเติมในคำตอบนั้นอาจเป็นรหัสตัวอย่างหรือคำถามรุ่นที่แก้ไข
Jithin Scaria

// รันสิ่งนี้ในโหนด v4 เพื่อดูพฤติกรรม "ที่คาดไว้" this.test = "ที่แนบมากับโมดูล"; var foo = {ทดสอบ: "แนบกับวัตถุ"}; foo.method = function (name, cb) {// ผูกค่าของ "this" บนเมธอด // เพื่อพยายามบังคับให้เป็นสิ่งที่คุณต้องการ [name] = cb.bind (this); }; foo.method ("bar", () => {console.log (this.test);}); foo.bar ();
Prithvi Raj Vuppalapati

สิ่งที่น่าสนใจคือให้ลองใช้แล้วลองอีกครั้งแทนที่ foo.method ('bar', () => {console.log (this.test);}); ด้วย foo.method ('bar', function () {console.log (this.test);}); - บันทึกเวอร์ชันแรก "แนบกับโมดูล" และบันทึกที่สอง "แนบกับอ็อบเจ็กต์" - ฉันชอบการรักษา "สิ่งนี้" ที่มีเสถียรภาพมากกว่าโดยใช้ฟังก์ชันลูกศร คุณยังคงสามารถบรรลุเอฟเฟกต์อื่น ๆ ได้โดยใช้รูปแบบที่แตกต่างกันและผลลัพธ์ก็คือ IMO ที่อ่านง่ายและคาดเดาได้มากขึ้น
Methodician

3

ฉันถามคำถามเดียวกันเมื่อสองสามวันก่อน

คุณไม่สามารถผูกค่าได้เนื่องจากthisถูกผูกไว้แล้ว

การผูกขอบเขตนี้แตกต่างกันกับตัวดำเนินการฟังก์ชัน ES6 =>


4
"เนื่องจากสิ่งนี้thisถูกผูกไว้แล้ว" - ในฟังก์ชันปกติจะถูกผูกไว้ด้วย ประเด็นคือในฟังก์ชันลูกศรจะไม่มีการผูกแบบโลคัล
ที่ดีกว่า

2

สั้นคุณไม่สามารถผูกฟังก์ชันลูกศรได้ แต่อ่านต่อ:

ลองนึกภาพว่าคุณมีฟังก์ชันลูกศรด้านล่างซึ่งจะพิมพ์thisบนคอนโซล:

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

ดังนั้นการแก้ไขด่วนสำหรับสิ่งนี้จะใช้ฟังก์ชันปกติดังนั้นเพียงแค่เปลี่ยนเป็น:

function myFunc() {console.log(this)};

จากนั้นคุณสามารถเชื่อมโยงกับสภาพแวดล้อมคำศัพท์โดยใช้bindหรือcallหรือapply:

const bindedFunc = myFunc.bind(this);

และเรียกมันในกรณีของbind.

bindedFunc();

นอกจากนี้ยังมีวิธีการที่จะใช้eval()ในการทำมันซึ่งขอไม่แนะนำ


function myFuncมีความแตกต่างทางพฤติกรรมในรูปแบบนอกเหนือจากการผูกมัด const myFunc = function() {...}การแข่งขันที่ใกล้ชิดจะเป็น ฉันยังอยากรู้ว่าคุณหมายถึงอะไรโดยใช้ eval เนื่องจากนั่นเป็นวิธีการที่ฉันไม่คิดว่าจะมีคำตอบใด ๆ ที่นี่มาก่อน - มันน่าสนใจที่จะดูว่าเสร็จสิ้นแล้วอ่านว่าทำไมจึงไม่ท้อถอยอย่างมาก
Florrie

1

ตัวอย่างนี้อาจช่วยคุณได้:

let bob = {
   _name: "Bob",
   _friends: ["stackoverflow"],
   printFriends:(x)=> {
      x._friends.forEach((f)=> {
         console.log(x._name + " knows " + f);
      });
   }
}

bob.printFriends = (bob.printFriends).bind(null,bob);
bob.printFriends();


gøød stuff ... intéréstingอย่างอ่อนโยน
Aleks
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.