Apa yang harus dilakukan ketika "ini" kehilangan tautan konteks

Halo, Habr! Saya mempersembahkan kepada Anda terjemahan artikel "Apa yang harus dilakukan ketika" ini "kehilangan konteks" oleh Cristi Salcescu .

Cara terbaik untuk menghindari kehilangan konteks ini adalah dengan tidak menggunakan ini . Namun, ini tidak selalu memungkinkan. Misalnya, kami bekerja dengan kode atau pustaka orang lain yang menggunakan ini .

Objek literal, fungsi konstruktor, konstruktor objek kelas dalam sistem prototipe. Parameter pseudo- ini digunakan dalam sistem prototyping untuk memberikan akses ke properti objek.

Mari kita lihat beberapa kasing.

Fungsi Bersarang


ini kehilangan referensi konteks di dalam fungsi bersarang.

class Service { constructor(){ this.numbers = [1,2,3]; this.token = "token"; } doSomething(){ setTimeout(function doAnotherThing(){ this.numbers.forEach(function log(number){ //Cannot read property 'forEach' of undefined console.log(number); console.log(this.token); }); }, 100); } } let service = new Service(); service.doSomething(); 

Metode doSomething () memiliki dua fungsi bersarang: doAnotherthing () dan log () . Saat memanggil service.doSomething () , ini kehilangan referensi konteks dalam fungsi bersarang.

bind ()


Salah satu cara untuk memecahkan masalah adalah dengan metode bind () . Lihatlah kode berikut:

 doSomething(){ setTimeout(function doAnotherThing(){ this.numbers.forEach(function log(number){ console.log(number); console.log(this.token); }.bind(this)); }.bind(this), 100); } 

bind () membuat versi baru dari fungsi, yang ketika dipanggil sudah memiliki nilai tertentu ini .

function doAnotherThing () {/*...*/}.bind(this) membuat versi dari fungsi doAnotherThing () , yang mengambil nilai ini dari doSomething () .

itu / diri


Opsi lain adalah mendeklarasikan dan menggunakan variabel baru itu / self , yang akan menyimpan nilai ini dari metode doSomething () .

 doSomething(){ let that = this; setTimeout(function doAnotherThing(){ that.numbers.forEach(function log(number){ console.log(number); console.log(that.token); }); }, 100); } 

Kita harus mendeklarasikan biarkan itu = ini di semua metode menggunakan ini di fungsi bersarang.

Fungsi panah


Fungsi panah memberi kita cara lain untuk menyelesaikan masalah ini.

 doSomething(){ setTimeout(() => { this.numbers.forEach(number => { console.log(number); console.log(this.token); }); }, 100); } 

Fungsi panah tidak membuat konteks sendiri untuk ini , melainkan menggunakan nilai ini dari konteks sekitarnya. Dalam contoh di atas, ia menggunakan nilai ini dari fungsi induk.

Kerugian dari metode ini adalah kita tidak dapat menentukan nama fungsi panah. Nama fungsi memainkan peran penting, karena meningkatkan keterbacaan kode dan menjelaskan tujuannya.

Di bawah ini adalah kode yang sama dengan fungsi yang dinyatakan dalam nama variabel:

 doSomething(){ let log = number => { console.log(number); console.log(this.token); } let doAnotherThing = () => { this.numbers.forEach(log); } setTimeout(doAnotherThing, 100); } 

Fungsi Panggilan Balik (Metode sebagai panggilan balik)


ini kehilangan referensi konteks ketika menggunakan metode ini sebagai fungsi panggilan balik. Mari kita lihat kelas berikut:

 class Service { constructor(){ this.token = "token"; } doSomething(){ console.log(this.token);//undefined } } let service = new Service(); 

Mari kita lihat situasi di mana metode service.doSomething () digunakan sebagai fungsi callback.

 //callback on DOM event $("#btn").click(service.doSomething); //callback for timer setTimeout(service.doSomething, 0); //callback for custom function run(service.doSomething); function run(fn){ fn(); } 

Dalam semua kasus di atas, ini kehilangan tautan konteks.

bind ()


Kita dapat menggunakan bind () untuk menyelesaikan masalah ini. Di bawah ini adalah kode untuk opsi ini:

 //callback on DOM event $("#btn").click(service.doSomething.bind(service)); //callback for timer setTimeout(service.doSomething.bind(service), 0); //callback for custom function run(service.doSomething.bind(service)); 

Fungsi panah


Cara lain adalah membuat fungsi panah yang memanggil service.doSomething () .

 //callback on DOM event $("#btn").click(() => service.doSomething()); //callback for timer setTimeout(() => service.doSomething(), 0); //callback for custom function run(() => service.doSomething()); 

Bereaksi Komponen


Dalam komponen, ini kehilangan referensi konteks ketika metode digunakan sebagai callback untuk acara.

 class TodoAddForm extends React.Component { constructor(){ super(); this.todos = []; } componentWillMount() { this.setState({desc: ""}); } add(){ let todo = {desc: this.state.desc}; //Cannot read property 'state' of undefined this.todos.push(todo); } handleChange(event) { //Cannot read property 'setState' of undefined this.setState({desc: event.target.value}); } render() { return <form> <input onChange={this.handleChange} value={this.state.desc} type="text"/> <button onClick={this.add} type="button">Save</button> </form>; } } ReactDOM.render( <TodoAddForm />, document.getElementById('root')); 

Sebagai solusinya, kita bisa membuat fungsi baru di konstruktor yang akan menggunakan bind (ini) .

 constructor(){ super(); this.todos = []; this.handleChange = this.handleChange.bind(this); this.add = this.add.bind(this); } 

Jangan gunakan "ini"


Tidak ini - tidak ada masalah dengan hilangnya konteks. Objek dapat dibuat menggunakan fungsi pabrik . Lihatlah contoh ini:

 function Service() { let numbers = [1,2,3]; let token = "token"; function doSomething(){ setTimeout(function doAnotherThing(){ numbers.forEach(function log(number){ console.log(number); console.log(token); }); }, 100); } return Object.freeze({ doSomething }); } 

Konteksnya tetap ada jika Anda menggunakan metode ini sebagai panggilan balik.

 let service = Service(); service.doSomething(); //callback on DOM event $("#btn").click(service.doSomething); //callback for timer setTimeout(service.doSomething, 0); //callback for custom function run(service.doSomething); 

Kesimpulan


ini kehilangan referensi konteks dalam berbagai situasi.
bind () , menggunakan variabel itu sendiri / dan fungsi panah adalah cara untuk memecahkan masalah konteks.

Fungsi pabrik memungkinkan untuk membuat objek tanpa menggunakan ini .

Source: https://habr.com/ru/post/id421959/


All Articles