JavaScript'te this
değerini bulmak zor olabilir. Bunu nasıl yapacağınız aşağıda açıklanmıştır.
JavaScript'in this
, oldukça karmaşık olduğu için birçok şakaya konu olur.
Ancak geliştiricilerin bu this
ile uğraşmamak için çok daha karmaşık ve alana özgü işlemler yaptığını gördüm. this
hakkında emin değilseniz bu bilgilerin işinize yarayacağını umuyoruz. Bu benim this
rehberim.
En belirgin durumdan başlayıp en belirsiz durumla bitireceğim. Bu makale büyük bir if (…) … else if () … else if (…) …
gibi olduğundan, baktığınız kodla eşleşen ilk bölüme doğrudan gidebilirsiniz.
- Fonksiyon ok işlevi olarak tanımlanmışsa
- Aksi takdirde, işlev/sınıf
new
ile çağrılırsa - Aksi takdirde, işlevin "bağlı" bir
this
değeri varsa - Aksi takdirde,
this
çağrı zamanında ayarlanmışsa - Aksi takdirde, işlev bir üst nesne (
parent.func()
) aracılığıyla çağrılırsa - Aksi takdirde, işlev veya üst kapsam katı moddaysa
- Aksi halde
İşlev ok işlevi olarak tanımlanmışsa:
const arrowFunction = () => {
console.log(this);
};
Bu durumda, this
değerinin 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, this
iç değeri değiştirilemediği ve her zaman dış this
ile aynı olduğu için mükemmeldir.
Diğer örnekler
Ok işlevleriyle this
değeri bind
ile değiştirilemez:
// Logs `true` - bound `this` value is ignored:
arrowFunction.bind({foo: 'bar'})();
Ok işlevlerinde 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, işlev başka bir nesnenin üyesi olarak çağrılarak this
değeri değiştirilemez:
const obj = {arrowFunction};
// Logs `true` - parent object is ignored:
obj.arrowFunction();
Ok işlevlerinde, işlev bir kurucu olarak çağrılarak this
değeri 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ğini ifade etmesini 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) etkinlik işleyiciler olarak örnek yöntemlerini kullanırken gerçekten faydalıdır.
Yukarıdaki kod, "this
, üst kapsamdaki this
ile aynı olacaktır" kuralını ihlal ediyor gibi görünebilir ancak sınıf alanlarını, oluşturucuda ayarları yapmak için kullanılan söz dizimi şekeri olarak düşünürseniz bu durum mantıklı gelmeye 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 kalıplar, mevcut bir işlevi oluşturucuda bağlamayı veya işlevi oluşturucuda atamayı içerir. Herhangi bir nedenle sınıf alanlarını kullanamıyorsanız oluşturucuda işlev atama makul bir alternatiftir:
class Whatever {
constructor() {
this.someMethod = () => {
// …
};
}
}
Aksi halde, işlev/sınıf new
ile çağrılırsa:
new Whatever();
Yukarıdaki kod, this
'un Object.create(Whatever.prototype)
sonucuna ayarlandığı Whatever
(veya sınıfsa oluşturucu işlevi) çağrır.
class MyClass {
constructor() {
console.log(
this.constructor === Object.create(MyClass.prototype).constructor,
);
}
}
// Logs `true`:
new MyClass();
Eski tarz kurucular için de aynı durum 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ğerinin bind
ile değiştirilmesi mümkün değildir:
const BoundMyClass = MyClass.bind({foo: 'bar'});
// Logs `true` - bound `this` value is ignored:
new BoundMyClass();
new
ile çağrıldığında, işlev başka bir nesnenin üyesi olarak çağrılarak this
değerinin değiştirilmesi mümkün değildir:
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
'ye (boundObject
) iletilen nesne olur.
// Logs `false`:
console.log(someFunction() === boundObject);
// Logs `true`:
console.log(boundFunction() === boundObject);
Diğer örnekler
Bağlı bir 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);
Bağlı bir işlev çağrılırken işlev başka bir nesnenin üyesi olarak çağrılarak this
değeri değiştirilemez:
const obj = {boundFunction};
// Logs `true` - parent object is ignored:
console.log(obj.boundFunction() === boundObject);
Aksi takdirde this
, arama zamanında ayarlanmışsa:
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ğerinin değeri, call
/apply
yöntemine iletilen nesnedir.
Maalesef this
, DOM etkinlik işleyicileri gibi öğeler tarafından başka bir değere ayarlanmış. Bu özelliğin kullanılması, anlaşılması zor 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
kullanmı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 durumda, işlev obj
öğesinin bir üyesi olarak çağrılır; dolayısıyla this
, obj
olur. Bu, çağrı sırasında gerçekleşir. Bu nedenle, işlev üst öğesi olmadan veya farklı bir üst öğeyle ç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
grubunun bir üyesi olarak çağrılmadığından someMethod() === obj
yanlıştır. Şuna benzer bir işlemi denerken bu hata ile karşılaşmış olabilirsiniz:
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
Bu durum, querySelector
uygulamasının kendi this
değerine bakması ve bunun bir tür DOM düğümü olmasını beklediği için bozulur ve yukarıdaki durum bu bağlantıyı bozar. Yukarıdakileri doğru şekilde gerçekleştirmek için:
const $ = document.querySelector.bind(document);
// Or:
const $ = (...args) => document.querySelector(...args);
İlginç bir bilgi: Tüm API'ler this
'yi şirket içinde kullanmaz. console.log
gibi Console yöntemleri, this
referanslarını önlemek için değiştirildi. Bu nedenle, log
'nin console
'a bağlanması gerekmez.
Aksi takdirde, işlev veya üst kapsam katı moddaysa:
function someFunction() {
'use strict';
return this;
}
// Logs `true`:
console.log(someFunction() === undefined);
Bu durumda, this
değeri tanımlanmamıştır. Üst kapsam katı modunda ise (ve tüm modüller katı moddaysa) işlevde 'use strict'
gerekmez.
Diğer durumlarda:
function someFunction() {
return this;
}
// Logs `true`:
console.log(someFunction() === globalThis);
Bu durumda, this
değerinin globalThis
ile aynı olduğu varsayılır.
Bora
Hepsi bu kadar! this
hakkında bildiğimiz her şey bu. Sorunuz mu var? Atladığımız bir konu var mı? Bana tweet atabilirsiniz.
İnceleme yapan Mathias Bynens, Ingvar Stepanyan ve Thomas Steiner'e teşekkürler.