Bir olay işleyicisini kaydeden bir kurucu işlevim var:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', function () {
alert(this.data);
});
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
Ancak, callback içinde oluşturulan nesnenin data
özelliğine erişemiyorum. Görünüşe göre this
oluşturulan nesneye değil, başka bir nesneye atıfta bulunuyor.
Ayrıca anonim bir fonksiyon yerine bir nesne metodu kullanmayı denedim:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
ancak aynı sorunları sergiliyor.
Doğru nesneye nasıl erişebilirim?
this
hakkında bilmeniz gerekenlerBu' (diğer adıyla "bağlam") her fonksiyonun içinde bulunan özel bir anahtar kelimedir ve değeri fonksiyonun nasıl/ne zaman/nerede tanımlandığına değil, sadece nasıl çağrıldığına bağlıdır. Diğer değişkenler gibi sözlüksel kapsamlardan etkilenmez (ok fonksiyonları hariç, aşağıya bakınız). İşte bazı örnekler:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
this
e nasıl başvurulur?this
Aslında özellikle this'e değil, *onun atıfta bulunduğu nesneye* erişmek istiyorsunuz. Bu nedenle kolay bir çözüm, bu nesneye de atıfta bulunan yeni bir değişken oluşturmaktır. Değişken herhangi bir isme sahip olabilir, ancak yaygın olanlar
selfve
that`tır.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
selfnormal bir değişken olduğundan, sözcüksel kapsam kurallarına uyar ve geriçağrının içinden erişilebilir. Bu aynı zamanda geri çağrının
this` değerine erişebilme avantajına da sahiptir.
this
değerini açıkça ayarlama - bölüm 1Değeri otomatik olarak ayarlandığı için this
değeri üzerinde hiçbir kontrolünüz yokmuş gibi görünebilir, ancak aslında durum böyle değildir.
Her fonksiyon .bind
[docs] yöntemine sahiptir ve bu yöntem this
değerine bağlı yeni bir fonksiyon döndürür. Fonksiyon, .bind
çağrısı yaptığınız fonksiyonla tamamen aynı davranışa sahiptir, sadece this
sizin tarafınızdan ayarlanmıştır. Bu fonksiyon nasıl veya ne zaman çağrılırsa çağrılsın, this
her zaman aktarılan değere atıfta bulunacaktır.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
Bu durumda, callback'in this
ini MyConstructor
'ın this
değerine bağlıyoruz.
Not: jQuery için bağlam bağlarken, bunun yerine jQuery.proxy
[docs] kullanın. Bunu yapmanın nedeni, bir olay geri çağrısının bağını çözerken fonksiyonun referansını saklamanıza gerek kalmamasıdır. jQuery bunu dahili olarak halleder.
ECMAScript 6, lambda fonksiyonları olarak düşünülebilecek ok fonksiyonlarını tanıtmaktadır. Bunların kendi this
bağları yoktur. Bunun yerine, this
normal bir değişken gibi kapsam içinde aranır. Bu da .bind
komutunu çağırmak zorunda olmadığınız anlamına gelir. Sahip oldukları tek özel davranış bu değildir, daha fazla bilgi için lütfen MDN belgelerine bakın.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
this
öğesini ayarlayın - bölüm 2Geri aramaları kabul eden bazı fonksiyonlar/yöntemler, geri aramanın this
değerine atıfta bulunması gereken bir değeri de kabul eder. Bu temelde kendiniz bağlamakla aynı şeydir, ancak işlev / yöntem bunu sizin için yapar. [Array#map
[docs]][map] böyle bir yöntemdir. İmzası şöyledir:
array.map(callback[, thisArg])
İlk bağımsız değişken geri çağırmadır ve ikinci bağımsız değişken this
değerinin başvurması gereken değerdir. İşte uydurma bir örnek:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
Not: this
için bir değer geçip geçemeyeceğiniz genellikle o fonksiyonun/yöntemin belgelerinde belirtilir. Örneğin, jQuery'nin $.ajax
yöntemi [docs] context
adlı bir seçeneği tanımlar:
Bu nesne Ajax ile ilgili tüm geri aramaların bağlamı haline getirilecektir.
Yaygın sorun: Nesne yöntemlerini geri arama/olay işleyici olarak kullanma
Bu sorunun bir başka yaygın görünümü de bir nesne yönteminin geri arama/olay işleyici olarak kullanılmasıdır. Fonksiyonlar JavaScript'te birinci sınıf vatandaşlardır ve "method" terimi bir nesne özelliğinin değeri olan bir fonksiyon için kullanılan günlük bir terimdir. Ancak bu fonksiyonun "containing" nesnesiyle belirli bir bağlantısı yoktur. Aşağıdaki örneği düşünün:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
Bu.methodişlevi tıklama olay işleyicisi olarak atanır, ancak
document.bodytıklanırsa, günlüğe kaydedilen değer
undefinedolur, çünkü olay işleyicisinin içinde
this,
Fooörneğini değil,
document.bodyörneğini ifade eder. Başta da belirtildiği gibi,
bu`nun neyi ifade ettiği, işlevin nasıl tanımlandığına değil, nasıl çağrıldığına bağlıdır.
Kod aşağıdaki gibi olsaydı, işlevin nesneye örtük bir referansa sahip olmadığı daha açık olabilirdi:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
Çözüm yukarıda belirtilenle aynıdır: Mümkünse, this
i belirli bir değere açıkça bağlamak için .bind
kullanın
document.body.onclick = this.method.bind(this);
veya anonim bir işlevi geri arama / olay işleyici olarak kullanarak işlevi nesnenin bir "yöntemi" olarak açıkça çağırın ve nesneyi (this
) başka bir değişkene atayın:
var self = this;
document.body.onclick = function() {
self.method();
};
veya bir ok fonksiyonu kullanın:
document.body.onclick = () => this.method();
Her şey bir metodu çağırmanın "sihirli" sözdizimindedir:
object.property();
Nesneden özelliği aldığınızda ve tek seferde çağırdığınızda, nesne yöntem için bağlam olacaktır. Aynı yöntemi ayrı adımlarla çağırırsanız, bağlam bunun yerine global kapsamdır (pencere):
var f = object.property;
f();
Bir metodun referansını aldığınızda, artık nesneye bağlı değildir, sadece düz bir fonksiyonun referansıdır. Geri arama olarak kullanmak için referans aldığınızda da aynı şey olur:
this.saveNextLevelData(this.setAll);
Bağlamı fonksiyona bağlayacağınız yer burasıdır:
this.saveNextLevelData(this.setAll.bind(this));
Eğer jQuery kullanıyorsanız, bind
tüm tarayıcılarda desteklenmediği için bunun yerine $.proxy
yöntemini kullanmalısınız:
this.saveNextLevelData($.proxy(this.setAll, this));
context" terimi bazen this tarafından başvurulan nesneye atıfta bulunmak için kullanılır. Kullanımı uygun değildir çünkü ECMAScript'in this ile ne anlamsal ne de teknik olarak uyuşmaz.
"Bağlam", anlam katan bir şeyi çevreleyen koşullar veya ekstra anlam veren bazı önceki ve sonraki bilgiler anlamına gelir. ECMAScript'te "context" terimi execution context, yani çalışan kodun kapsamındaki tüm parametreler, kapsam ve this anlamında kullanılır.
Bu durum ECMA-262 bölüm 10.4.2'de gösterilmiştir:
ThisBinding öğesini, ThisBinding öğesinin değeriyle aynı değere ayarlayın. yürütme bağlamını çağırma
bu da bunun bir yürütme bağlamının parçası olduğunu açıkça gösterir.
Bir yürütme bağlamı, yürütülmekte olan koda anlam katan çevre bilgilerini sağlar. Sadece thisBinding'ten çok daha fazla bilgi içerir.
Yani this değeri "context" değildir, bir yürütme bağlamının sadece bir parçasıdır. Esasen, çağrı tarafından herhangi bir nesneye ve sıkı modda herhangi bir değere ayarlanabilen yerel bir değişkendir.