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
kılavuzum.
En belirgin durumdan başlayıp en belirsiz durumla bitireceğim. Bu makale bir nevi büyük bir if (…) … else if () … else if (…) …
gibidir. Bu nedenle, doğrudan baktığınız kodla eşleşen ilk bölüme 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şlevlerinde 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öntemlerinde, this
'ün her zaman sınıf örneğine atıfta bulunduğundan emin olmak istiyorsanız en iyi yöntem 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 dinleyicileri olarak örnek yöntemleri kullanırken gerçekten yararlı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 takdirde, 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
görüşme 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 dinleyicileri gibi öğeler tarafından başka bir değere ayarlanır ve kullanılması, anlaşılması zor kodlara 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 durumda işlev, obj
üyesi olarak çağrılır. Bu nedenle 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
'in üyesi olarak çağrılmadığı için someMethod() === obj
yanlıştır. Aşağıdaki gibi bir işlem yaparken bu sorunla karşılaşmış olabilirsiniz:
const $ = document.querySelector;
// TypeError: Illegal invocation
const el = $('.some-element');
Bu, querySelector
uygulamasının kendi this
değerine bakması ve bunun bir tür DOM düğümü olmasını beklemesi ve yukarıdakilerin bu bağlantıyı bozması nedeniyle bozulur. Yukarıdakileri doğru şekilde yapmak 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ından kaçınmak 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ı modda (ve tüm modüller katı modda) ise işlevde 'use strict'
gerekmez.
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ğimiz tek ş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.