JavaScript'te this
değerini anlamak zor olabilir. Nasıl yapıldığını buradan öğrenebilirsiniz...
JavaScript'in this
, şakanın en önemli kısmıdır ve bu nedenle oldukça karmaşıktır.
Ancak, geliştiricilerin bu this
sorunuyla karşılaşmamak için çok daha karmaşık ve alana özgü işlemler yaptığını gözlemledim. this
konusunda emin değilseniz bu bilginin işinize yarayacağını umuyorum. Bu benim this
rehberim.
En ayrıntılı şekilde başlayıp en az spesifik olanla bitireceğim. Bu makale büyük bir if (…) … else if () … else if (…) …
gibi olduğundan doğrudan baktığınız kodla eşleşen ilk bölüme gidebilirsiniz.
- İşlev bir ok işlevi olarak tanımlanırsa
- Aksi halde, işlev/sınıf
new
ile çağrılırsa - Aksi halde, işlevin "bağlı"
this
değeri varsa - Aksi takdirde,
this
arama anında ayarlanırsa - Aksi halde, işlev bir üst nesne (
parent.func()
) aracılığıyla çağrılırsa - Aksi halde işlev veya üst kapsam, yüksek düzey modundaysa
- Aksi halde
İşlev bir ok işlevi olarak tanımlanırsa:
const arrowFunction = () => {
console.log(this);
};
Bu durumda, this
değeri her zaman üst kapsamdaki this
ile aynıdır:
const outerThis = this;
const arrowFunction = () => {
// Always logs `true`:
console.log(this === outerThis);
};
Ok işlevleri harikadır çünkü this
iç değeri değiştirilemez, dış this
ile her zaman aynıdır.
Diğer örnekler
Ok işlevleri kullanıldığında this
değeri bind
ile değiştirilemez:
// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
Ok işlevleri kullanıldığında this
değeri call
veya apply
ile değiştirilemez:
// Logs `true` - called `this` value is ignored:
arrowFunction.call({foo: 'bar'});
// Logs `true` - applied `this` value is ignored:
arrowFunction.apply({foo: 'bar'});
Ok işlevlerinde this
değeri, işlev başka bir nesnenin üyesi olarak çağrılarak değiştirilemez:
const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
Ok işlevlerinde this
değeri, işlev bir kurucu olarak çağrılarak değiştirilemez:
// TypeError: arrowFunction is not a constructor
new arrowFunction();
"Bağlı" örnek yöntemleri
Örnek yöntemleriyle, this
öğesinin her zaman sınıf örneğine başvurduğundan emin olmak istiyorsanız en iyi yol, ok işlevlerini ve sınıf alanlarını kullanmaktır:
class Whatever {
someMethod = () => {
// Always the instance of Whatever:
console.log(this);
};
}
Bu kalıp, bileşenlerde (React bileşenleri veya web bileşenleri gibi) olay işleyiciler olarak örnek yöntemleri kullanırken gerçekten yararlıdır.
Yukarıdaki yöntem, "this
, üst kapsamda this
ile aynı olacak" kuralını ihlal ediyormuş gibi görünebilir, ancak sınıf alanlarını, oluşturucuda bazı öğeleri ayarlamak için kullanılan sözdizimsel şeker olarak düşünürseniz bu durum anlamlı olmaya başlar:
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);
};
}
}
Alternatif yamalar, oluşturucuda mevcut bir işlevi bağlamayı veya işlevin oluşturucuda atanmasını içerir. Herhangi bir nedenle sınıf alanlarını kullanamıyorsanız oluşturucuda işlev atamak makul bir alternatiftir:
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
Aksi takdirde, işlev/sınıf new
ile çağrılırsa:
new Whatever();
Yukarıdaki kural, this
parametresinin Object.create(Whatever.prototype)
sonucuna ayarlanmış olarak Whatever
(veya sınıfsa oluşturucu işlevini) çağırır.
class MyClass {
constructor() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
}
// Logs `true`:
new MyClass();
Aynı durum, eski tarz oluşturucular için de geçerlidir:
function MyClass() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
// Logs `true`:
new MyClass();
Diğer örnekler
new
ile çağrıldığında this
değeri bind
ile değiştirilemez:
const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
new
ile çağrıldığında, this
değeri işlev başka bir nesnenin üyesi olarak çağrılarak değiştirilemez:
const obj = {MyClass};
// Logs `true` - parent object is ignored:
new obj.MyClass();
Aksi takdirde, işlevin "bağlı" bir this
değeri varsa:
function someFunction() {
return this;
}
const boundObject = {hello: 'world'};
const boundFunction = someFunction.bind(boundObject);
boundFunction
her çağrıldığında, this
değeri bind
(boundObject
) öğesine geçirilen nesne olur.
// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
Diğer örnekler
Bir bağlı işlev çağrılırken this
değeri call
veya
apply
ile değiştirilemez:
// 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);
Bir bağlı işlev çağrılırken this
değeri, işlev başka bir nesnenin üyesi olarak çağrılarak değiştirilemez:
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
Aksi takdirde, this
arama zamanında ayarlanırsa:
function someFunction() {
return this;
}
const someObject = {hello: 'world'};
// Logs `true`:
console.log(someFunction.call(someObject) === someObject);
// Logs `true`:
console.log(someFunction.apply(someObject) === someObject);
this
değeri, call
/apply
işlevine iletilen nesnedir.
Maalesef this
, DOM etkinlik işleyicileri gibi şeyler tarafından başka bir değere ayarlandığı için kullanılması zor bir koda neden olabilir:
element.addEventListener('click', function (event) { // Logs `element`, since the DOM spec sets `this` to // the element the handler is attached to. console.log(this); });
Yukarıdaki gibi durumlarda this
kullanmaktan kaçınıyorum ve bunun yerine:
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); });
Aksi takdirde, işlev bir üst nesne (parent.func()
) aracılığıyla çağrılırsa:
const obj = {
someMethod() {
return this;
},
};
// Logs `true`:
console.log(obj.someMethod() === obj);
Bu örnekte, işlev obj
öğesinin bir üyesi olarak çağrılır; dolayısıyla this
, obj
olur. Bu durum, çağrı zamanında gerçekleşir. Bu nedenle, işlev üst nesnesi olmadan veya farklı bir üst nesne ile çağrılırsa bağlantı bozulur:
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
üyesi olarak çağrılmadığından someMethod() === obj
yanlıştır. Şuna benzer bir şey denerken
bu hata ile karşılaşmış olabilirsiniz:
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
Bu hata, querySelector
uygulamasının kendi this
değerine bakması, bunun bir tür DOM düğümü olmasını beklediği ve yukarıdaki bağlantı bu bağlantıyı koparmasına neden olur. Yukarıdakileri doğru şekilde gerçekleştirmek için:
const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
Eğlenceli bir gerçek: Tüm API'ler dahili olarak this
kullanmaz. console.log
gibi konsol yöntemleri, this
referanslarını önleyecek şekilde değiştirilmiştir. Bu nedenle, log
öğesinin console
öğesine bağlanmasına gerek yoktur.
Aksi takdirde, işlev veya üst kapsam yüksek düzey modundaysa:
function someFunction() {
'use strict';
return this;
}
// Logs `true`:
console.log(someFunction() === undefined);
Bu durumda, this
değeri tanımsızdır. Üst kapsam yüksek düzey modunda (ve tüm modüller yüksek düzey modundaysa) işlevde 'use strict'
gerekli değildir.
Diğer durumlarda:
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
Bu durumda, this
değeri globalThis
ile aynıdır.
Bora
Hepsi bu kadar! this
hakkında bildiğim tüm bu yok. Sorunuz mu var? Atladığımız bir şey var mı? İsterseniz bana tweet atabilirsiniz.
İncelemeleri için Mathias Bynens, Ingvar Stepanyan ve Thomas Steiner'a teşekkür ederiz.