Mengetahui nilai this
bisa jadi sulit dalam JavaScript, berikut cara melakukannya...
this
JavaScript adalah awal dari banyak lelucon, dan itu karena memang cukup rumit.
Namun, saya melihat developer melakukan hal yang jauh lebih rumit dan khusus domain agar tidak menangani this
ini. Jika Anda tidak yakin tentang this
, semoga ini dapat membantu. Ini adalah panduan this
saya.
Saya akan mulai dengan situasi yang paling
spesifik, dan mengakhirinya dengan yang paling tidak spesifik. Artikel ini
mirip seperti if (…) … else if () … else if (…) …
besar, jadi Anda dapat langsung ke bagian
pertama yang cocok dengan kode yang sedang 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
disetel pada waktu panggilan - Atau, jika fungsi dipanggil melalui objek induk (
parent.func()
) - Atau, jika fungsi atau cakupan induk berada 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, nilai selalu sama
dengan this
luar.
Contoh lain
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 'Terikat'
Dengan metode instance, jika Anda ingin memastikan this
selalu merujuk pada instance class, cara terbaiknya 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 melanggar aturan "this
akan sama dengan this
dalam cakupan induk",
tetapi hal tersebut mulai masuk akal jika Anda menganggap kolom class sebagai sugar sintaksis untuk menetapkan berbagai 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);
};
}
}
Paten alternatif melibatkan binding fungsi yang ada di konstruktor, atau menetapkan fungsi di dalam konstruktor. Jika Anda tidak dapat menggunakan kolom class karena alasan tertentu, menetapkan fungsi di konstruktor adalah alternatif yang wajar:
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
Atau, jika fungsi/class dipanggil dengan new
:
new Whatever();
Hal di atas akan memanggil Whatever
(atau fungsi konstruktornya jika berupa class) dengan this
yang 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 lama:
function MyClass() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
// Logs `true`:
new MyClass();
Contoh lain
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();
Sebaliknya, 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 lain
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 tersebut sebagai anggota objek lain:
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
Atau, jika this
disetel 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
disetel ke beberapa nilai lain oleh hal-hal seperti pemroses peristiwa DOM, dan penggunaannya 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); });
Atau, jika fungsi dipanggil melalui objek induk (parent.func()
):
const obj = {
someMethod() {
return this;
},
};
// Logs `true`:
console.log(obj.someMethod() === obj);
Dalam hal ini, fungsi tersebut 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
salah karena someMethod
tidak dipanggil sebagai anggota obj
. Anda mungkin
menemukan kesalahan saat mencoba sesuatu seperti ini:
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
Hal ini rusak karena implementasi querySelector
melihat nilai this
-nya sendiri dan mengharapkannya
sebagai jenis node DOM, dan implementasi di atas memutus 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 ke console
.
Sebaliknya, 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 terdefinisi. 'use strict'
tidak diperlukan dalam fungsi ini jika
cakupan induk berada dalam mode
ketat (dan semua
modul dalam mode ketat).
Atau:
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
Dalam hal ini, nilai this
sama dengan globalThis
.
Fiuh!
Dan selesai! Itu yang saya tahu tentang this
. Ada pertanyaan? Sesuatu yang saya lewatkan? Jangan ragu
untuk menyampaikan saya.
Terima kasih kepada Mathias Bynens, Ingvar Stepanyan, dan Thomas Steiner yang telah memberikan ulasan.