Mencari tahu nilai this
bisa jadi rumit di JavaScript. Berikut cara melakukannya…
this
JavaScript menjadi bahan lelucon, dan itu karena, yah, cukup rumit.
Namun, saya telah melihat developer melakukan hal-hal yang jauh lebih rumit dan khusus domain untuk menghindari
menangani this
ini. Jika Anda tidak yakin dengan this
, semoga informasi ini dapat membantu. Ini adalah panduan this
saya.
Saya akan memulai dengan situasi yang paling spesifik, dan mengakhiri dengan situasi yang paling tidak spesifik. Artikel ini
seperti if (…) … else if () … else if (…) …
besar, sehingga Anda dapat langsung membuka bagian
pertama yang cocok dengan kode yang Anda lihat.
- Jika fungsi ditentukan sebagai fungsi panah
- Atau, jika fungsi/class dipanggil dengan
new
- Atau, jika fungsi memiliki nilai
this
'terikat' - Atau, jika
this
ditetapkan pada waktu panggilan - Atau, jika fungsi dipanggil melalui objek induk (
parent.func()
) - Atau, jika fungsi atau cakupan induk dalam mode ketat
- Atau
Jika fungsi ditentukan sebagai fungsi panah:
const arrowFunction = () => {
console.log(this);
};
Dalam hal ini, nilai this
selalu sama dengan this
dalam cakupan induk:
const outerThis = this;
const arrowFunction = () => {
// Always logs `true`:
console.log(this === outerThis);
};
Fungsi panah sangat bagus karena nilai dalam this
tidak dapat diubah, dan selalu sama dengan this
bagian luar.
Contoh lainnya
Dengan fungsi panah, nilai this
tidak dapat diubah dengan bind
:
// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
Dengan fungsi panah, nilai this
tidak dapat diubah dengan call
atau apply
:
// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});
Dengan fungsi panah, nilai this
tidak dapat diubah dengan memanggil fungsi sebagai anggota objek lain:
const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
Dengan fungsi panah, nilai this
tidak dapat diubah dengan memanggil fungsi sebagai
konstruktor:
// TypeError: arrowFunction is not a constructor
new arrowFunction();
Metode instance 'Bound'
Dengan metode instance, jika Anda ingin memastikan this
selalu merujuk ke instance class, cara
terbaik adalah menggunakan fungsi panah dan kolom
class:
class Whatever {
someMethod = () => {
// Always the instance of Whatever:
console.log(this);
};
}
Pola ini sangat berguna saat menggunakan metode instance sebagai pemroses peristiwa dalam komponen (seperti komponen React, atau komponen web).
Hal di atas mungkin terasa seperti melanggar aturan "this
akan sama dengan this
dalam cakupan induk",
tetapi hal ini mulai masuk akal jika Anda menganggap kolom class sebagai sintaksis yang mudah untuk menetapkan hal-hal
dalam konstruktor:
class Whatever {
someMethod = (() => {
const outerThis = this;
return () => {
// Always logs `true`:
console.log(this === outerThis);
};
})();
}
// …is roughly equivalent to:
class Whatever {
constructor() {
const outerThis = this;
this.someMethod = () => {
// Always logs `true`:
console.log(this === outerThis);
};
}
}
Patten alternatif melibatkan binding fungsi yang ada di konstruktor, atau menetapkan fungsi dalam konstruktor. Jika Anda tidak dapat menggunakan kolom class karena alasan tertentu, menetapkan fungsi dalam konstruktor adalah alternatif yang wajar:
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
Jika tidak, jika fungsi/class dipanggil dengan new
:
new Whatever();
Kode di atas akan memanggil Whatever
(atau fungsi konstruktornya jika berupa class) dengan this
ditetapkan ke
hasil Object.create(Whatever.prototype)
.
class MyClass {
constructor() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
}
// Logs `true`:
new MyClass();
Hal yang sama berlaku untuk konstruktor gaya yang lebih lama:
function MyClass() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
// Logs `true`:
new MyClass();
Contoh lainnya
Saat dipanggil dengan new
, nilai this
tidak dapat diubah dengan bind
:
const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
Saat dipanggil dengan new
, nilai this
tidak dapat diubah dengan memanggil fungsi sebagai anggota
objek lain:
const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();
Jika tidak, jika fungsi memiliki nilai this
'terikat':
function someFunction() {
return this;
}
const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);
Setiap kali boundFunction
dipanggil, nilai this
-nya akan menjadi objek yang diteruskan ke bind
(boundObject
).
// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
Contoh lainnya
Saat memanggil fungsi terikat, nilai this
tidak dapat diubah dengan call
atau
apply
:
// Logs `true` - called `this` value is ignored:
console.log(boundFunction.call({foo: 'bar'}) === boundObject);
// Logs `true` - applied `this` value is ignored:
console.log(boundFunction.apply({foo: 'bar'}) === boundObject);
Saat memanggil fungsi terikat, nilai this
tidak dapat diubah dengan memanggil fungsi sebagai anggota objek lain:
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
Jika tidak, jika this
ditetapkan pada waktu panggilan:
function someFunction() {
return this;
}
const someObject = {hello: 'world'};
// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);
Nilai this
adalah objek yang diteruskan ke call
/apply
.
Sayangnya, this
ditetapkan ke beberapa nilai lain oleh hal-hal seperti pemroses peristiwa DOM, dan menggunakannya dapat
menghasilkan kode yang sulit dipahami:
element.addEventListener('click', function (event) { // Logs `element`, since the DOM spec sets `this` to // the element the handler is attached to. console.log(this); });
Saya menghindari penggunaan this
dalam kasus seperti di atas, dan sebagai gantinya:
element.addEventListener('click', (event) => { // Ideally, grab it from a parent scope: console.log(element); // But if you can't do that, get it from the event object: console.log(event.currentTarget); });
Jika tidak, jika fungsi dipanggil melalui objek induk (parent.func()
):
const obj = {
someMethod() {
return this;
},
};
// Logs `true`:
console.log(obj.someMethod() === obj);
Dalam hal ini, fungsi dipanggil sebagai anggota obj
sehingga this
akan menjadi obj
. Hal ini terjadi pada
waktu panggilan, sehingga link akan rusak jika fungsi dipanggil tanpa objek induknya, atau dengan
objek induk yang berbeda:
const {someMethod} = obj;
// Logs `false`:
console.log(someMethod() === obj);
const anotherObj = {someMethod};
// Logs `false`:
console.log(anotherObj.someMethod() === obj);
// Logs `true`:
console.log(anotherObj.someMethod() === anotherObj);
someMethod() === obj
bernilai salah karena someMethod
tidak dipanggil sebagai anggota obj
. Anda mungkin
telah mengalami masalah ini saat mencoba sesuatu seperti ini:
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
Hal ini rusak karena penerapan querySelector
melihat nilai this
-nya sendiri dan mengharapkannya
menjadi semacam node DOM, dan hal di atas akan memutuskan koneksi tersebut. Untuk mencapai hal di atas dengan benar:
const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
Fakta menarik: Tidak semua API menggunakan this
secara internal. Metode konsol seperti console.log
diubah untuk
menghindari referensi this
, sehingga log
tidak perlu terikat dengan console
.
Jika tidak, jika fungsi atau cakupan induk berada dalam mode ketat:
function someFunction() {
'use strict';
return this;
}
// Logs `true`:
console.log(someFunction() === undefined);
Dalam hal ini, nilai this
tidak ditentukan. 'use strict'
tidak diperlukan dalam fungsi jika
cakupan induk berada dalam mode
ketat (dan semua
modul berada dalam mode ketat).
Atau:
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
Dalam hal ini, nilai this
sama dengan globalThis
.
Fiuh!
Selesai. Itu semua yang saya tahu tentang this
. Ada pertanyaan? Ada yang saya lewatkan? Jangan
ragu untuk mengirim tweet kepada saya.
Terima kasih kepada Mathias Bynens, Ingvar Stepanyan, dan Thomas Steiner atas tinjauan ini.