Bagaimana cara mengakses `this` yang benar di dalam callback?

1425

Saya memiliki fungsi konstruktor yang mendaftarkan pengendali acara:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', function () {
        alert(this.data);
    });
}

// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};

// called as
var obj = new MyConstructor('foo', transport);

Namun, saya tidak dapat mengakses dataproperti objek yang dibuat di dalam callback. Sepertinya thistidak merujuk ke objek yang dibuat tetapi ke yang lain.

Saya juga mencoba menggunakan metode objek alih-alih fungsi anonim:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', this.alert);
}

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

tapi itu menunjukkan masalah yang sama.

Bagaimana saya bisa mengakses objek yang benar?

Felix Kling
sumber
95
Dari waktu ke waktu saya sangat muak dengan jenis pertanyaan tertentu, sehingga saya memutuskan untuk menulis jawaban kanonik. Meskipun pertanyaan-pertanyaan ini telah dijawab sebanyak jutaan kali, tidak selalu mungkin untuk menemukan pasangan pertanyaan + jawaban yang baik yang tidak “dicemari” oleh informasi yang tidak relevan. Ini adalah salah satu momen dan salah satu pertanyaan itu (dan saya bosan). Jika Anda berpikir bahwa sebenarnya ada pertanyaan / jawaban kanonik yang ada untuk jenis pertanyaan ini, beri tahu saya dan saya akan menghapus yang ini. Saran untuk perbaikan dipersilahkan!
Felix Kling
3
Halaman TypeScript yang berguna tentang ini , sebagian besar juga berlaku untuk JS.
Ondra Žižka

Jawaban:

1791

Apa yang harus Anda ketahui this

this(alias "konteks") adalah kata kunci khusus di dalam setiap fungsi dan nilainya hanya tergantung pada bagaimana fungsi itu dipanggil, bukan bagaimana / kapan / di mana ia didefinisikan. Itu tidak dipengaruhi oleh lingkup leksikal seperti variabel lain (kecuali untuk fungsi panah, lihat di bawah). Berikut ini beberapa contohnya:

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

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

Untuk mempelajari lebih lanjut this, lihat dokumentasi MDN .


Cara merujuk ke yang benar this

Jangan gunakan this

Anda sebenarnya tidak ingin mengakses thissecara khusus, tetapi objek yang dimaksud . Itu sebabnya solusi mudah adalah dengan membuat variabel baru yang juga merujuk ke objek itu. Variabel dapat memiliki nama apa saja, tetapi yang umum adalah selfdan that.

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

Karena selfmerupakan variabel normal, ia mematuhi aturan lingkup leksikal dan dapat diakses di dalam callback. Ini juga memiliki keuntungan bahwa Anda dapat mengakses thisnilai dari panggilan balik itu sendiri.

Atur thispanggilan balik secara eksplisit - bagian 1

Mungkin terlihat seperti Anda tidak memiliki kontrol atas nilai thiskarena nilainya diatur secara otomatis, tetapi sebenarnya tidak demikian.

Setiap fungsi memiliki metode .bind [docs] , yang mengembalikan fungsi baru yang thisterikat ke suatu nilai. Fungsi ini memiliki perilaku yang persis sama dengan yang Anda panggil .bind, hanya itu thisyang Anda tetapkan. Tidak peduli bagaimana atau kapan fungsi itu dipanggil, thisakan selalu merujuk pada nilai yang diteruskan.

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

Dalam hal ini, kami mengikat panggilan balik thiske nilai MyConstructor's this.

Catatan: Saat mengikat konteks untuk jQuery, gunakan jQuery.proxy [docs] sebagai gantinya. Alasan untuk melakukan ini adalah agar Anda tidak perlu menyimpan referensi ke fungsi tersebut saat melepaskan ikatan peristiwa. jQuery menanganinya secara internal.

ECMAScript 6: Gunakan fungsi panah

ECMAScript 6 memperkenalkan fungsi panah , yang dapat dianggap sebagai fungsi lambda. Mereka tidak memiliki thisikatan sendiri . Sebaliknya, thisdilihat dalam lingkup seperti variabel normal. Itu berarti Anda tidak perlu menelepon .bind. Itu bukan satu-satunya perilaku khusus yang mereka miliki, silakan lihat dokumentasi MDN untuk informasi lebih lanjut.

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

Set thispanggilan balik - bagian 2

Beberapa fungsi / metode yang menerima panggilan balik juga menerima nilai yang menjadi thisacuan panggilan balik itu . Ini pada dasarnya sama dengan mengikatnya sendiri, tetapi fungsi / metode melakukannya untuk Anda. Array#map [Docs] adalah metode semacam itu. Tanda tangannya adalah:

array.map(callback[, thisArg])

Argumen pertama adalah callback dan argumen kedua adalah nilai yang thisharus dirujuk. Berikut adalah contoh yang dibuat-buat:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

Catatan: Apakah Anda dapat memberikan nilai atau tidak thisbiasanya disebutkan dalam dokumentasi fungsi / metode tersebut. Misalnya, metode jQuery [docs]$.ajax menjelaskan opsi yang disebut context:

Objek ini akan dijadikan konteks semua panggilan balik terkait Ajax.


Masalah umum: Menggunakan metode objek sebagai callback / event handler

Manifestasi umum lain dari masalah ini adalah ketika metode objek digunakan sebagai pemanggil balik / penangan kejadian. Fungsinya adalah warga negara kelas satu dalam JavaScript dan istilah "metode" hanyalah istilah sehari-hari untuk fungsi yang merupakan nilai properti objek. Tetapi fungsi itu tidak memiliki tautan spesifik ke objek "mengandung" nya.

Perhatikan contoh berikut:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

Fungsi this.methodditugaskan sebagai pengendali event klik, tetapi jika document.bodydiklik, nilai yang dicatat akan undefined, karena di dalam pengendali event, thismerujuk pada document.body, bukan instance dari Foo.
Seperti yang telah disebutkan di awal, apa yang thismerujuk tergantung pada bagaimana fungsi dipanggil , bukan bagaimana itu didefinisikan .
Jika kodenya seperti berikut ini, mungkin lebih jelas bahwa fungsi tersebut tidak memiliki referensi implisit ke objek:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

Solusinya sama dengan yang disebutkan di atas: Jika tersedia, gunakan .binduntuk secara eksplisit mengikat thiske nilai tertentu

document.body.onclick = this.method.bind(this);

atau secara eksplisit memanggil fungsi sebagai "metode" objek, dengan menggunakan fungsi anonim sebagai callback / event handler dan menetapkan objek ( this) ke variabel lain:

var self = this;
document.body.onclick = function() {
    self.method();
};

atau gunakan fungsi panah:

document.body.onclick = () => this.method();
Felix Kling
sumber
39
Felix, saya sudah membaca jawaban ini sebelumnya tetapi tidak pernah menjawab. Saya menjadi khawatir bahwa orang menggunakan selfdan thatmerujuk this. Saya merasakan hal ini karena thismerupakan variabel kelebihan beban yang digunakan dalam konteks yang berbeda; sedangkan selfbiasanya sesuai dengan instance lokal dan thatbiasanya merujuk ke objek lain. Saya tahu Anda tidak menetapkan aturan ini, seperti yang saya lihat muncul di beberapa tempat lain, tetapi juga mengapa saya mulai menggunakan _this, tetapi saya tidak yakin bagaimana perasaan orang lain, kecuali untuk praktik yang tidak seragam yang telah menghasilkan.
vol7ron
3
@ Feliksling apakah aman untuk mengasumsikan bahwa menggunakan fungsi-fungsi prototipe di dalam ini akan selalu memiliki perilaku yang diharapkan terlepas dari bagaimana mereka (biasanya) dipanggil? Saat menggunakan callback di dalam fungsi prototipe, apakah ada alternatif untuk mengikat (), sendiri atau itu?
andig
5
@ Feliksling Kadang-kadang bisa berguna untuk mengandalkan Function.prototype.call ()dan Function.prototype.apply (). Terutama dengan apply ()saya mendapatkan banyak jarak tempuh. Saya kurang cenderung untuk menggunakan bind ()mungkin hanya karena kebiasaan meskipun saya sadar (tetapi tidak yakin) bahwa mungkin ada sedikit kelebihan biaya overhead untuk menggunakan bind atas opsi lain.
Nolo
5
Jawaban yang bagus tetapi pertimbangkan untuk menambahkan solusi opsional tambahan yang tidak menggunakan kelas, baru, atau ini sama sekali.
Aluan Haddad
4
kembali panah fungsi "Alih-alih, ini terlihat dalam lingkup seperti variabel normal." benar-benar membuat klik ini untuk saya, terima kasih! () => this.clicked();)
alphanumeric0101
211

Berikut adalah beberapa cara untuk mengakses konteks orang tua di dalam konteks anak -

  1. Anda dapat menggunakan bind()fungsi.
  2. Simpan referensi ke konteks / ini di dalam variabel lain (lihat contoh di bawah).
  3. Gunakan fungsi ES6 Arrow .
  4. Ubah kode / fungsi desain / arsitektur - untuk ini, Anda harus memiliki perintah atas pola desain dalam javascript.

1. Gunakan bind()fungsi

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', ( function () {
        alert(this.data);
    }).bind(this) );
}
// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};
// called as
var obj = new MyConstructor('foo', transport);

Jika Anda menggunakan underscore.js- http://underscorejs.org/#bind

transport.on('data', _.bind(function () {
    alert(this.data);
}, this));

2 Simpan referensi ke konteks / ini di dalam variabel lain

function MyConstructor(data, transport) {
  var self = this;
  this.data = data;
  transport.on('data', function() {
    alert(self.data);
  });
}

3 Fungsi panah

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
Mohan Dere
sumber
1
Opsi bind () luar biasa hanya dengan melewati pointer dari Objek ini menjadi objek ini di objek lain (: Terima kasih!
Stav Bodik
Bind () berfungsi seperti mantra. Terima kasih banyak +1 dari saya :)
Anjana Silva
56

Semuanya dalam sintaks "ajaib" memanggil metode:

object.property();

Ketika Anda mendapatkan properti dari objek dan memanggilnya dalam sekali jalan, objek akan menjadi konteks untuk metode ini. Jika Anda memanggil metode yang sama, tetapi dalam langkah-langkah terpisah, konteksnya adalah ruang lingkup global (jendela) sebagai gantinya:

var f = object.property;
f();

Ketika Anda mendapatkan referensi metode, itu tidak lagi melekat pada objek, itu hanya referensi ke fungsi polos. Hal yang sama terjadi ketika Anda mendapatkan referensi untuk digunakan sebagai panggilan balik:

this.saveNextLevelData(this.setAll);

Di situlah Anda akan mengikat konteks ke fungsi:

this.saveNextLevelData(this.setAll.bind(this));

Jika Anda menggunakan jQuery, Anda harus menggunakan $.proxymetode ini, karena bindtidak didukung di semua browser:

this.saveNextLevelData($.proxy(this.setAll, this));
Guffa
sumber
33

Masalah dengan "konteks"

Istilah "konteks" kadang-kadang digunakan untuk merujuk ke objek yang dirujuk oleh ini . Penggunaannya tidak tepat karena tidak cocok baik secara semantik maupun teknis dengan ECMAScript's ini .

"Konteks" berarti keadaan di sekitar sesuatu yang menambah makna, atau informasi sebelumnya dan berikut yang memberikan makna tambahan. Istilah "konteks" digunakan dalam ECMAScript untuk merujuk ke konteks eksekusi , yang merupakan semua parameter, cakupan, dan ini dalam lingkup beberapa kode pelaksana.

Ini ditunjukkan dalam ECMA-262 bagian 10.4.2 :

Atur ThisBinding ke nilai yang sama dengan ThisBinding dari konteks eksekusi panggilan

yang jelas menunjukkan bahwa ini adalah bagian dari konteks eksekusi.

Konteks eksekusi memberikan informasi di sekitarnya yang menambahkan makna pada kode yang sedang dieksekusi. Ini mencakup lebih banyak informasi dari pada sekadar Binding ini .

Jadi nilai ini bukan "konteks", itu hanya satu bagian dari konteks eksekusi. Ini pada dasarnya variabel lokal yang dapat diatur oleh panggilan ke objek apa pun dan dalam mode ketat, ke nilai apa pun.

RobG
sumber
Tidak setuju dengan jawaban ini. Keberadaan istilah "konteks eksekusi" tidak melarang penggunaan "konteks" lainnya selain melarang penggunaan "eksekusi" lainnya. Mungkin ada istilah yang lebih baik untuk dijelaskan thistetapi tidak ada yang ditawarkan di sini, dan ini sudah terlambat untuk menutup pintu pada "konteks".
Roamer-1888
@ Roamer-1888 — terima kasih atas hasil edit. Anda benar, tetapi argumen saya tidak bergantung pada keberadaan "konteks eksekusi" yang menghalangi "konteks" untuk tujuan lain. Alih-alih, ini didasarkan pada "konteks" yang tidak sesuai dari sudut pandang teknis dan semantik. Saya juga berpikir penggunaan "konteks" bukannya "ini" sedang sekarat. Saya tidak melihat alasan untuk menemukan istilah alternatif untuk ini atau thisBinding , itu hanya obfuscates dan sarana di beberapa titik Anda harus menjelaskan bahwa "konteks" sebenarnya ini , dan bahwa hal itu tidak dengan cara apapun "konteks". :-)
RobG
Saya tidak berpikir Anda bisa mengatakan bahwa ini bukan "konteks" dengan cara apa pun, ketika Anda telah mengakui bahwa itu adalah salah satu bagian dari konteks eksekusi, di mana "eksekusi" hanyalah kata sifat.
Roamer-1888
@ Roamer-1888 — Saya tidak akan melanjutkan percakapan ini melewati titik ini. Ya, ini adalah bagian dari konteks eksekusi. Mengatakan itu dalam konteks seperti mengatakan satu pemain dari tim adalah tim.
RobG
RobG, Sayang sekali Anda tidak ingin melanjutkan. Ini debat yang menarik. Terima kasih telah memberikan saya waktu Anda.
Roamer-1888
31

Anda harus tahu tentang kata kunci "ini".

Sesuai pandangan saya Anda dapat menerapkan "ini" dalam tiga cara (fungsi Self / Arrow / Metode Bind)

Fungsi kata kunci ini berperilaku sedikit berbeda dalam JavaScript dibandingkan dengan bahasa lain.

Ini juga memiliki beberapa perbedaan antara mode ketat dan mode non-ketat.

Dalam kebanyakan kasus, nilai ini ditentukan oleh bagaimana suatu fungsi dipanggil.

Itu tidak dapat diatur oleh tugas selama eksekusi, dan mungkin berbeda setiap kali fungsi dipanggil.

ES5 memperkenalkan metode bind () untuk mengatur nilai fungsi ini terlepas dari bagaimana namanya,

dan ES2015 memperkenalkan fungsi panah yang tidak menyediakan pengikatannya sendiri (ini mempertahankan nilai konteks leksikal terlampir ini).

Metode 1: Self - Self digunakan untuk mempertahankan referensi ke aslinya bahkan ketika konteksnya sedang berubah. Ini adalah teknik yang sering digunakan dalam event handler (terutama pada penutupan).

Referensi : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function () {
        alert(self.data);
    });
}

Metode2 : Fungsi panah - Ekspresi fungsi panah adalah alternatif yang ringkas secara sintaksis untuk ekspresi fungsi biasa,

meskipun tanpa ikatan sendiri dengan ini, argumen, kata kunci super, atau new.target.

Ekspresi fungsi panah tidak cocok sebagai metode, dan mereka tidak dapat digunakan sebagai konstruktor.

Referensi : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

  function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',()=> {
        alert(this.data);
    });
}

Method3 : Bind- The bind () method menciptakan fungsi baru yang,

ketika dipanggil, kata kunci ini disetel ke nilai yang disediakan,

dengan urutan argumen tertentu sebelum diberikan ketika fungsi baru dipanggil.

Referensi: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

  function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',(function() {
        alert(this.data);
    }).bind(this);
Ashish
sumber
25

Pertama, Anda harus memiliki pemahaman scopedan perilaku thiskata kunci yang jelas dalam konteks scope.

this& scope:


there are two types of scope in javascript. They are :

   1) Global Scope

   2) Function Scope

singkatnya, lingkup global mengacu pada objek jendela. Variabel yang dideklarasikan dalam lingkup global dapat diakses dari mana saja. Di lain pihak, ruang lingkup fungsi berada di dalam suatu fungsi. Variabel yang dideklarasikan di dalam suatu fungsi tidak dapat diakses dari dunia luar secara normal. thiskata kunci dalam lingkup global mengacu pada objek jendela. thisfungsi dalam juga merujuk ke objek jendela. Jadi thisakan selalu merujuk ke jendela sampai kita menemukan cara untuk memanipulasi thisuntuk menunjukkan konteks yang kita pilih sendiri.

--------------------------------------------------------------------------------
-                                                                              -
-   Global Scope                                                               -
-   ( globally "this" refers to window object)                                 -     
-                                                                              -
-         function outer_function(callback){                                   -
-                                                                              -
-               // outer function scope                                        -
-               // inside outer function"this" keyword refers to window object -                                                                              -
-              callback() // "this" inside callback also refers window object  -

-         }                                                                    -
-                                                                              -
-         function callback_function(){                                        -
-                                                                              -
-                //  function to be passed as callback                         -
-                                                                              -
-                // here "THIS" refers to window object also                   -
-                                                                              -
-         }                                                                    -
-                                                                              -
-         outer_function(callback_function)                                    -
-         // invoke with callback                                              -
--------------------------------------------------------------------------------

Berbagai cara untuk memanipulasi thisfungsi panggilan balik di dalam:

Di sini saya memiliki fungsi konstruktor bernama Person. Ia memiliki sifat yang disebut namedan empat metode yang disebut sayNameVersion1, sayNameVersion2, sayNameVersion3, sayNameVersion4. Keempatnya memiliki satu tugas khusus. Terima panggilan balik dan aktifkan panggilan itu. Panggilan balik memiliki tugas khusus yaitu mencatat properti nama dari instance fungsi konstruktor Person.

function Person(name){

    this.name = name

    this.sayNameVersion1 = function(callback){
        callback.bind(this)()
    }
    this.sayNameVersion2 = function(callback){
        callback()
    }

    this.sayNameVersion3 = function(callback){
        callback.call(this)
    }

    this.sayNameVersion4 = function(callback){
        callback.apply(this)
    }

}

function niceCallback(){

    // function to be used as callback

    var parentObject = this

    console.log(parentObject)

}

Sekarang mari kita membuat instance dari person constructor dan memanggil versi yang berbeda dari sayNameVersionX(X merujuk ke 1,2,3,4) metode dengan niceCallbackuntuk melihat berapa banyak cara kita dapat memanipulasi thiscallback dalam untuk merujuk ke personinstance.

var p1 = new Person('zami') // create an instance of Person constructor

mengikat:

Apa yang mengikat lakukan adalah membuat fungsi baru dengan thiskata kunci diatur ke nilai yang disediakan.

sayNameVersion1dan sayNameVersion2gunakan bind untuk memanipulasi thisfungsi callback.

this.sayNameVersion1 = function(callback){
    callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
    callback()
}

yang pertama mengikat thisdengan callback di dalam metode itu sendiri. Dan untuk yang kedua callback dilewatkan dengan objek terikat padanya.

p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method

p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback

hubungi:

The first argumentdari callmetode yang digunakan sebagai thisdalam fungsi yang dipanggil dengan callmelekat padanya.

sayNameVersion3gunakan calluntuk memanipulasi thisuntuk merujuk ke objek orang yang kita buat, bukan objek jendela.

this.sayNameVersion3 = function(callback){
    callback.call(this)
}

dan itu disebut seperti berikut:

p1.sayNameVersion3(niceCallback)

menerapkan :

Mirip dengan call, argumen pertama applymerujuk pada objek yang akan ditunjukkan oleh thiskata kunci.

sayNameVersion4gunakan applyuntuk memanipulasi thisuntuk merujuk ke objek orang

this.sayNameVersion4 = function(callback){
    callback.apply(this)
}

dan itu disebut seperti berikut ini. Hanya panggilan balik yang dilewati,

p1.sayNameVersion4(niceCallback)
AL-zami
sumber
1
kritik konstruktif apa pun mengenai jawabannya akan dihargai!
AL-zami
1
Kata kunci ini dalam lingkup global tidak selalu merujuk ke objek jendela . Itu hanya berlaku di browser.
Randall Flagg
1
@RallallFlagg saya menulis jawaban ini dari sudut pandang peramban.
Tolong bebaskan
19

Kami tidak dapat mengikat ini setTimeout(), karena selalu dijalankan dengan objek global (Jendela) , jika Anda ingin mengakses thiskonteks dalam fungsi panggilan balik maka dengan menggunakan bind()ke fungsi panggilan balik kita dapat mencapai sebagai:

setTimeout(function(){
    this.methodName();
}.bind(this), 2000);
Datta Chanewad
sumber
9
Bagaimana ini berbeda dari jawaban yang ada?
Felix Kling
13

Pertanyaannya berkisar bagaimana thisperilaku kata kunci dalam javascript. thisberperilaku berbeda seperti di bawah ini,

  1. Nilai thisbiasanya ditentukan oleh konteks eksekusi fungsi.
  2. Dalam lingkup global, thismengacu pada objek global ( windowobjek).
  3. Jika mode ketat diaktifkan untuk fungsi apa pun maka nilai thisakan undefinedseperti dalam mode ketat, objek global mengacu undefinedpada tempat windowobjek.
  4. Objek yang berdiri sebelum titik adalah apa yang akan terikat oleh kata kunci ini.
  5. Kita bisa mengatur nilai ini secara eksplisit dengan call(), bind(), danapply()
  6. Ketika newkata kunci digunakan (konstruktor), ini terikat ke objek baru yang sedang dibuat.
  7. Fungsi Panah tidak mengikat this - sebagai gantinya, thisterikat secara leksikal (yaitu berdasarkan pada konteks asli)

Seperti yang disarankan sebagian besar jawaban, kita dapat menggunakan fungsi Panah atau bind()Metode atau var Diri . Saya akan mengutip poin tentang lambdas (fungsi Panah) dari Panduan Gaya JavaScript Google

Lebih suka menggunakan fungsi panah di atas f.bind (ini), dan terutama di atas goog.bind (f, ini). Hindari menulis const self = ini. Fungsi panah sangat berguna untuk panggilan balik, yang terkadang melewati argumen tambahan yang tidak terduga.

Google jelas merekomendasikan untuk menggunakan lambdas daripada mengikat atau const self = this

Jadi solusi terbaik adalah dengan menggunakan lambdas seperti di bawah ini,

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}

Referensi:

  1. https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
  2. panah-fungsi-vs-mengikat
Code_Mode
sumber
Pertanyaan ini secara khusus tentang penggunaan fungsi / metode sebagai panggilan balik. Jawaban Anda mungkin lebih cocok untuk stackoverflow.com/q/3127429/218196 .
Felix Kling
@FelixKling Ya pertanyaannya adalah tentang menggunakan fungsi / metode sebagai panggilan balik dalam masalah utama adalah karena penanganan thiskata kunci itulah sebabnya saya membagi jawaban saya menjadi dua bagian, satu tentang thisdan kedua tentang menggunakan fungsi / metode sebagai panggilan balik. Jangan ragu untuk mengedit jawabannya.
Code_Mode
Saya menemukan poin keempat Anda diucapkan secara ambigu. Pertimbangkan contoh “Masalah Saat Menggunakan Metode dengan Objek Ini sebagai Callbacks” , di mana objek yang tepat berdiri di depan titik, tetapi masih konteksnya bukan objek itu.
bleistift2
7

Saat ini ada pendekatan lain yang mungkin jika kelas digunakan dalam kode.

Dengan dukungan bidang kelas dimungkinkan untuk membuatnya dengan cara berikutnya:

class someView {
    onSomeInputKeyUp = (event) => {
        console.log(this); // this refers to correct value
    // ....
    someInitMethod() {
        //...
        someInput.addEventListener('input', this.onSomeInputKeyUp)

Pasti di bawah tenda itu semua fungsi panah baik tua yang mengikat konteks tetapi dalam bentuk ini terlihat jauh lebih jelas bahwa pengikatan eksplisit.

Karena itu Proposal Tahap 3 Anda akan memerlukan babel dan plugin babel yang sesuai untuk memprosesnya seperti untuk saat ini (08/2018).

skyboyer
sumber
2
Ini persis seperti cara saya membuatnya bekerja di dalam Script: public methodName = (params) => { body }di dalam kelas.
yeyeyerman
5

Pendekatan lain, yang merupakan cara standar sejak DOM2 untuk mengikat thisdalam pendengar acara, yang memungkinkan Anda selalu menghapus pendengar (di antara manfaat lainnya), adalah handleEvent(evt)metode dari EventListenerantarmuka:

var obj = {
  handleEvent(e) {
    // always true
    console.log(this === obj);
  }
};

document.body.addEventListener('click', obj);

Informasi terperinci tentang penggunaan handleEventdapat ditemukan di sini: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38

Andrea Puddu
sumber
0

this dalam JS:

Nilai thisdalam JS adalah 100% ditentukan oleh bagaimana suatu fungsi dipanggil, dan bukan bagaimana itu didefinisikan. Kita dapat dengan relatif mudah menemukan nilai thisoleh 'left of the dot rule' :

  1. Ketika fungsi dibuat menggunakan fungsi kata kunci, nilai dari thisadalah objek yang tersisa dari titik fungsi yang disebut
  2. Jika tidak ada objek yang tersisa dari titik maka nilai thisdi dalam suatu fungsi sering merupakan objek global ( globaldalam node, windowdi browser). Saya tidak akan merekomendasikan menggunakan thiskata kunci di sini karena kurang eksplisit daripada menggunakan sesuatu seperti window!
  3. Ada konstruksi tertentu seperti fungsi panah dan fungsi yang dibuat menggunakan Function.prototype.bind()fungsi yang dapat memperbaiki nilai this. Ini adalah pengecualian dari aturan tetapi sangat membantu untuk memperbaiki nilai this.

Contoh dalam nodeJS

module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);

const obj1 = {
    data: "obj1 data",
    met1: function () {
        console.log(this.data);
    },
    met2: () => {
        console.log(this.data);
    },
};

const obj2 = {
    data: "obj2 data",
    test1: function () {
        console.log(this.data);
    },
    test2: function () {
        console.log(this.data);
    }.bind(obj1),
    test3: obj1.met1,
    test4: obj1.met2,
};

obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);

Keluaran:

masukkan deskripsi gambar di sini

Biarkan saya memandu Anda melalui output 1 oleh 1 (mengabaikan log pertama mulai dari yang kedua):

  1. thisadalah obj2karena di sebelah kiri titik aturan, kita dapat melihat bagaimana test1disebut obj2.test1();. obj2yang tersisa dari titik dan dengan demikian thisnilainya.
  2. Meskipun obj2dibiarkan di titik, test2terikat obj1melalui bind()metode. Jadi thisnilainya obj1.
  3. obj2yang tersisa dari dot dari fungsi yang disebut: obj2.test3(). Karena itu obj2akan menjadi nilai this.
  4. Dalam hal ini: obj2.test4() obj2berada di sebelah kiri titik. Namun fungsi panah tidak memiliki thisikatan sendiri . Oleh karena itu akan mengikat thisnilai lingkup luar yang merupakan module.exportsobjek yang dicatat di awal.
  5. Kami juga dapat menentukan nilai thisdengan menggunakan callfungsi. Di sini kita dapat memberikan nilai yang diinginkan thissebagai argumen, yang obj2dalam hal ini.
Willem van der Veen
sumber