Bisakah Anda mengikat 'ini' dalam fungsi panah?

133

Saya telah bereksperimen dengan ES6 untuk sementara waktu sekarang, dan saya baru saja mengalami sedikit masalah.

Saya sangat suka menggunakan fungsi panah, dan kapan pun saya bisa, saya menggunakannya.

Namun, tampaknya Anda tidak dapat mengikatnya!

Inilah fungsinya:

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

Inilah objek yang ingin saya ikat fungsinya:

var o = {'a': 42};

Dan di sini adalah bagaimana saya akan mengikat fke o:

var fBound = f.bind(o);

Dan kemudian saya bisa menelepon fBound:

fBound();

Yang akan menampilkan ini ( oobjek):

{'a': 42}

Keren! Menyenangkan! Kecuali itu tidak berhasil. Alih-alih mengeluarkan oobjek, itu menghasilkan windowobjek.

Jadi saya ingin tahu: bisakah Anda mengikat fungsi panah? (Dan jika demikian, bagaimana?)


Saya sudah menguji kode di atas di Google Chrome 48 dan Firefox 43, dan hasilnya sama.

Florrie
sumber
29
Seluruh titik fungsi panah adalah bahwa mereka menggunakan thislingkup induknya.
loganfsmyth

Jawaban:

158

Anda tidak dapat mem - rebindthis di fungsi panah. Itu akan selalu didefinisikan sebagai konteks di mana ia didefinisikan. Jika Anda thisharus bermakna, Anda harus menggunakan fungsi normal.

Dari ECMAScript 2015 Spec :

Referensi apa pun untuk argumen, super, ini, atau new.target di dalam ArrowFunction harus diselesaikan dengan pengikatan di lingkungan yang tertutup secara leksikal. Biasanya ini akan menjadi Lingkungan Fungsi dari fungsi segera melampirkan.

Nick Tomlin
sumber
7
Kalimat pertama jawaban ini salah. Mungkin karena pertanyaannya juga membingungkan dan mengikat this. Keduanya terkait, tetapi tidak sama. thisdi dalam fungsi panah selalu 'mewarisi' thisdari lingkup melampirkan. Itu adalah fitur fungsi panah. Tapi Anda masih bisa bindsemua parameter lain dari fungsi panah. Hanya tidak this.
Stijn de Witt
54

Agar lengkap, Anda dapat mengikat kembali fungsi panah, Anda tidak bisa mengubah artinya this.

bind masih memiliki nilai untuk argumen fungsi:

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

Cobalah di sini: http://jsbin.com/motihanopi/edit?js,console

cvazac
sumber
1
Apakah ada cara untuk mendeteksi atau menggunakan objek apa pun yang terikat fungsi (panah), tanpa referensi this(yang, tentu saja, didefinisikan secara leksikal)?
Florrie
3
Gunakan argumen untuk konteks: ((konteks) => {someOtherFunction.apply (konteks)}) .bind (willBeIgnored, context) ()
cvazac
13

Dari MDN :

Ekspresi fungsi panah memiliki sintaks yang lebih pendek dibandingkan dengan ekspresi fungsi dan secara leksikal mengikat nilai ini (tidak mengikat sendiri ini, argumen, super, atau new.target). Fungsi panah selalu anonim.

Ini berarti Anda tidak dapat mengikat nilai thisseperti yang Anda inginkan.

Sterling Archer
sumber
6

Selama bertahun-tahun, pengembang js berjuang dengan pengikatan konteks, bertanya mengapa thisberubah dalam javascript, begitu banyak kebingungan selama bertahun-tahun karena pengikatan konteks dan perbedaan antara makna thisdalam javascript dan thisdi sebagian besar bahasa OOP lainnya.

Semua ini membuat saya bertanya, mengapa, mengapa! mengapa Anda tidak ingin mengubah fungsi panah! Yang dibuat khusus untuk menyelesaikan semua masalah dan kebingungan ini dan menghindari keharusan menggunakan bindatau callatau cara lain apa pun untuk mempertahankan ruang lingkup fungsi.

TL; DR

Tidak, Anda tidak dapat me-rebind fungsi panah.

taxicala
sumber
7
Fungsi panah diciptakan terlebih dahulu untuk menyediakan sintaks yang lebih bersih dan lebih cepat. Pikirkan lambda-calculus. Sayang sekali OO-fanatik yang mengganggu kehidupan programmer JS fungsional mengambil kesempatan untuk mengambil kebebasan untuk menentukan konteks doa.
Marco Faustinelli
5
Penggunaan thistidak fungsional. lambda seharusnya arguments => output. Jika Anda membutuhkan konteks eksternal, sampaikan. Keberadaan thisinilah yang memfasilitasi semua penyemaian pola OO ke dalam bahasa. Anda tidak akan pernah mendengar istilah "kelas javascript" tanpa itu.
erich2k8
3
Anda dapat mengubah fungsi panah. Hanya tidak this.
Stijn de Witt
6

Anda tidak dapat menggunakan binduntuk mengubah nilai thisdi dalam fungsi panah. Namun, Anda dapat membuat fungsi reguler baru yang melakukan hal yang sama dengan fungsi panah lama dan kemudian menggunakan callatau binduntuk mengikat kembali thisseperti biasa.

Kami menggunakan evalpanggilan di sini untuk membuat kembali fungsi panah yang Anda berikan sebagai fungsi normal dan kemudian gunakan calluntuk memanggilnya dengan yang berbeda this:

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

nama
sumber
3
Terima kasih telah membagikan contoh ini tentang cara mengikat fungsi panah ke konteks yang berbeda! Untuk membuat jawabannya lebih mudah dipahami oleh pembaca di masa mendatang, mungkin Anda dapat mengeditnya untuk menjelaskan bagaimana dan mengapa kode ini bekerja?
Florrie
4

Lakukan Fungsi Panah ES6 Benar-Benar Memecahkan "ini" Dalam JavaScript

Tautan di atas menjelaskan bahwa fungsi panah thistidak berubah dengan bind, call, applyfungsi.

Ini dijelaskan dengan contoh yang sangat bagus.

jalankan ini node v4untuk melihat perilaku "yang diharapkan",

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(); 
Prithvi Raj Vuppalapati
sumber
Meminta Anda untuk memberikan informasi yang lebih relevan dalam jawaban itu sendiri, mungkin kode contoh atau versi pertanyaan yang diedit
Jithin Scaria
// jalankan ini di simpul v4 untuk melihat perilaku "yang diharapkan" this.test = "terpasang ke modul"; var foo = {test: "melekat pada objek"}; foo.method = fungsi (nama, cb) {// mengikat nilai "ini" pada metode // untuk mencoba dan memaksanya menjadi apa yang Anda inginkan ini [nama] = cb.bind (ini); }; foo.method ("bar", () => {console.log (this.test);}); foo.bar ();
Prithvi Raj Vuppalapati
Yang menarik di sini adalah untuk mencoba ini dan kemudian coba lagi mengganti foo.method ('bar', () => {console.log (this.test);}); dengan foo.method ('bar', function () {console.log (this.test);}); - log versi pertama "terlampir pada modul" dan log kedua "melekat pada objek" - Saya benar-benar lebih suka perlakuan yang lebih stabil dari "ini" menggunakan fungsi panah. Anda masih dapat mencapai efek lain menggunakan pola yang berbeda dan hasilnya IMO lebih mudah dibaca dan diprediksi.
Metodis
3

Saya mengajukan pertanyaan yang sama beberapa hari yang lalu.

Anda tidak dapat mengikat nilai karena thissudah diikat.

Binding berbeda cakupan ini ke ES6 => operator fungsi

fos.alex
sumber
4
"sejak ini sudah diikat" - Dalam fungsi biasa thisjuga terikat. Intinya adalah bahwa dalam fungsi panah tidak memiliki ikatan lokal .
oliver yang lebih baik
2

Pendek, Anda TIDAK BISA mengikat fungsi panah, tetapi baca terus:

Bayangkan Anda memiliki fungsi panah di bawah ini yang dicetak thispada konsol:

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

Jadi perbaikan cepat untuk ini akan menggunakan fungsi normal, jadi ubah saja ke:

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

Kemudian Anda dapat mengikatnya ke lingkungan leksikal menggunakan bindatau callatau apply:

const bindedFunc = myFunc.bind(this);

dan menyebutnya jika bind.

bindedFunc();

Ada juga cara menggunakan eval()untuk melakukannya, yang sangat tidak dianjurkan .

Alireza
sumber
function myFuncsecara perilaku berbeda dalam cara selain dapat diikat; pertandingan yang lebih dekat akan const myFunc = function() {...}. Saya juga ingin tahu apa yang Anda maksudkan dengan menggunakan eval, karena itu adalah pendekatan yang saya pikir tidak ada jawaban yang dibagikan di sini sebelumnya - itu akan menarik untuk melihat bagaimana hal itu dilakukan, dan kemudian membaca mengapa hal itu sangat tidak dianjurkan.
Florrie
1

Mungkin contoh ini membantu Anda:

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();

Ehsan
sumber
hal-hal yang aneh ... interésting ringan
Aleks