Bir geri arama içinde doğru "buna" nasıl erişilir?
Bir olay işleyicisini kaydeden bir yapıcı 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, data
geri arama içinde oluşturulan nesnenin özelliğine erişemiyorum . Görünüşe göre this
yaratılan nesneye değil, başka birine atıfta bulunuluyor.
Ayrıca anonim bir işlev yerine bir nesne yöntemi kullanmayı denedim:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
ama aynı sorunları sergiliyor.
Doğru nesneye nasıl erişebilirim?
Yanıtlar
Bilmen gerekenler this
this
(aka "bağlam") Her işlev içinde özel bir anahtar kelime ve onun değeri yalnızca bağlıdır nasıl işlevi tanımlandı zaman / nerede değil nasıl /, adlandırıldı. Diğer değişkenler gibi sözlü 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`
Daha fazla bilgi edinmek this
için MDN belgelerine göz atın .
Doğru olana nasıl başvurulur this
Ok işlevlerini kullanın
ECMAScript 6 , lambda işlevleri olarak düşünülebilecek ok işlevlerini tanıttı . Kendi this
bağları yok. Bunun yerine, this
normal bir değişken gibi kapsamda bakılır. Bu, aramanıza gerek olmadığı anlamına gelir .bind
. 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));
}
Kullanma this
Aslında this
özellikle erişmek istemezsiniz , ancak başvurduğu nesneye . Bu yüzden kolay bir çözüm, o nesneye de atıfta bulunan yeni bir değişken yaratmaktır. Değişkenin herhangi bir adı olabilir, ancak yaygın olanlar self
ve that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Yana self
normal bir değişkendir, bu sözcük kapsam kuralları uyar ve geri arama içeride erişilebilir. Bu aynı zamanda this
geri aramanın değerine erişebilme avantajına da sahiptir .
Geri aramanın açıkça ayarlanması this
- bölüm 1
this
Değeri otomatik olarak ayarlandığı için değeri üzerinde kontrolünüz yokmuş gibi görünebilir , ancak aslında durum böyle değildir.
Her işlev, bir değere bağlı yeni bir işlev döndüren .bind
[docs] yöntemine sahiptir this
. Fonksiyon, çağırdığınızla tamamen aynı davranışa sahiptir .bind
, sadece this
sizin tarafınızdan ayarlanmış olan şey. Bu işlev nasıl ve ne zaman çağrılırsa çağrılsın, this
her zaman aktarılan değeri ifade eder.
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, geri aramaları 's this
değerine MyConstructor
bağlıyoruz this
.
Not: jQuery için bir bağlama bağlamı oluşturduğunuzda, bunun yerine jQuery.proxy
[docs] kullanın. Bunu yapmanın nedeni, bir olay geri aramasını çözerken işleve olan başvuruyu depolamanıza gerek kalmamasıdır. jQuery bunu dahili olarak halleder.
this
Geri arama seti - 2. bölüm
Geri aramaları kabul eden bazı işlevler / yöntemler, geri aramaların this
başvurması gereken bir değeri de kabul eder. Bu temelde onu kendiniz bağlamakla aynıdır, ancak işlev / yöntem bunu sizin için yapar. Array#map
[docs] böyle bir yöntemdir. İmzası:
array.map(callback[, thisArg])
İlk argüman geri çağırmadır ve ikinci argüman this
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: için bir değer this
iletip iletemeyeceğiniz genellikle o işlevin / yöntemin belgelerinde belirtilir. Örneğin, jQuery'nin $.ajax
yöntemi [docs] , şu adı verilen bir seçeneği açıklar context
:
Bu nesne, Ajax ile ilgili tüm geri aramaların bağlamı haline getirilecektir.
Yaygın sorun: Nesne yöntemlerini geri çağırmalar / olay işleyicileri olarak kullanma
Bu sorunun diğer bir yaygın tezahürü, bir nesne yönteminin geri çağırma / olay işleyicisi olarak kullanılmasıdır. Fonksiyonlar, JavaScript'in birinci sınıf vatandaşlarıdır ve "yöntem" terimi, bir nesne özelliğinin değeri olan bir fonksiyon için yalnızca konuşma dilinde kullanılan bir terimdir. Ancak bu işlevin "içeren" nesnesine 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);
};
Fonksiyon this.method
tıklama olay işleyicisi olarak atanan, ancak eğer bir document.body
tıklandığında, değeri olacaktır günlüğe undefined
olay işleyicisi içinde, çünkü this
atıfta document.body
arasında değil, örneğin Foo
.
Başlangıçta daha önce bahsedildiği gibi, neyin this
ifade edildiği işlevin nasıl tanımlandığına değil, nasıl çağrıldığına bağlıdır .
Kod aşağıdaki gibiyse, işlevin nesneye örtük bir başvurusu olmadığı daha açık olabilir:
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: Varsa, belirli bir değere .bind
açıkça bağlanmak this
için kullanın
document.body.onclick = this.method.bind(this);
veya anonim bir işlevi geri çağırma / olay işleyicisi olarak kullanarak ve nesneyi ( this
) başka bir değişkene atayarak, işlevi nesnenin bir "yöntemi" olarak açıkça çağırın :
var self = this;
document.body.onclick = function() {
self.method();
};
veya bir ok işlevi kullanın:
document.body.onclick = () => this.method();
Alt bağlamda üst içeriğe erişmenin birkaç yolu:
bind()
İşlevi kullanabilirsiniz .- Bağlama referansı / bunu başka bir değişkenin içinde saklayın (aşağıdaki örneğe bakın).
- ES6 Ok işlevlerini kullanın .
- Kod / işlev tasarımını / mimarisini değiştirin - bunun için javascript'teki tasarım kalıpları üzerinde komuta sahip olmalısınız .
1. bind()
işlevi kullanın
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', ( function () {
alert(this.data);
}).bind(this) );
}
// Mock transport object
var transport = {
on: function(event, callback) {
setTimeout(callback, 1000);
}
};
// called as
var obj = new MyConstructor('foo', transport);
underscore.js
- http://underscorejs.org/#bind kullanıyorsanız
transport.on('data', _.bind(function () {
alert(this.data);
}, this));
2 Bağlama referans / bunu başka bir değişkenin içinde saklayın
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
3 Ok işlevi
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
Hepsi bir yöntemi çağırmanın "sihirli" sözdiziminde:
object.property();
Nesneden özelliği aldığınızda ve bir seferde çağırdığınızda, nesne yöntemin bağlamı olacaktır. Aynı yöntemi, ancak ayrı adımlarda çağırırsanız, bağlam bunun yerine genel kapsamdır (pencere):
var f = object.property;
f();
Bir yöntemin referansını aldığınızda, artık nesneye eklenmez, yalnızca düz bir işleve referanstır. Geri arama olarak kullanılacak referansı aldığınızda da aynısı olur:
this.saveNextLevelData(this.setAll);
Bağlamı işleve bağlayacağınız yer burasıdır:
this.saveNextLevelData(this.setAll.bind(this));
JQuery kullanıyorsanız, tüm tarayıcılarda desteklenmediği için $.proxy
bunun yerine yöntemi kullanmalısınız bind
:
this.saveNextLevelData($.proxy(this.setAll, this));
"Bağlam" ile ilgili sorun
"Bağlam" terimi bazen tarafından başvurulan nesne başvurmak için kullanılır bu . Onunla semantik veya teknik ya uymuyor çünkü Onun kullanılması uygun değildir ECMAScript en bu .
"Bağlam" , anlam katan bir şeyi çevreleyen koşullar veya fazladan anlam veren bazı önceki ve sonraki bilgiler anlamına gelir. "Bağlam" terimi, ECMAScript'te , tüm parametreler, kapsam ve bu , bazı yürütme kodlarının kapsamındaki yürütme bağlamına atıfta bulunmak için kullanılır .
Bu, ECMA-262 bölüm 10.4.2'de gösterilmektedir :
ThisBinding'i çağıran yürütme bağlamının ThisBindingiyle aynı değere ayarlayın
Bu , 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 çevreleyen bilgileri sağlar. Yalnızca thisBinding'den çok daha fazla bilgi içerir .
Değeri Yani bu "bağlam" değil, bir yürütme içeriği sadece bir parçası. Esasen herhangi bir nesneye yapılan çağrı ile ve katı modda herhangi bir değere ayarlanabilen yerel bir değişkendir.
"Bu" Anahtar Kelimeyi bilmelisiniz.
Benim görüşüme göre "bunu" üç şekilde uygulayabilirsiniz (Self / Arrow function / Bind Method)
Bir işlevin this anahtar sözcüğü JavaScript'te diğer dillere kıyasla biraz farklı davranır.
Katı mod ile katı olmayan mod arasında da bazı farklılıklar vardır.
Çoğu durumda, bunun değeri bir fonksiyonun nasıl çağrıldığına göre belirlenir.
Yürütme sırasında atama ile ayarlanamaz ve işlev her çağrıldığında farklı olabilir.
ES5, nasıl çağrıldığına bakılmaksızın bir işlevin değerini ayarlamak için bind () yöntemini tanıttı.
ve ES2015, kendisine ait bu bağlamayı sağlamayan ok işlevlerini tanıttı (çevreleyen sözcüksel bağlamın bu değerini korur).
Yöntem 1: Öz - Benlik, bağlam değişirken bile orijinaline bir referansı sürdürmek için kullanılıyor. Genellikle olay işleyicilerinde (özellikle kapanışlarda) kullanılan bir tekniktir.
Referans : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function () {
alert(self.data);
});
}
Yöntem2 : Ok işlevi - Ok işlevi ifadesi, normal işlev ifadesine sözdizimsel olarak kompakt bir alternatiftir,
buna, argümanlara, süper veya new.target anahtar kelimelerine kendi bağlantıları olmasa da.
Ok işlevi ifadeleri yöntem olarak uygun değildir ve yapıcılar olarak kullanılamazlar.
Referans : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',()=> {
alert(this.data);
});
}
Method3 : Bind- bind () yöntemi,
çağrıldığında, bu anahtar kelimesi sağlanan değere ayarlanır,
yeni işlev çağrıldığında sağlanan herhangi bir argüman dizisi ile.
Referans: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind
function MyConstructor(data, transport) {
this.data = data;
transport.on('data',(function() {
alert(this.data);
}).bind(this);
Öncelikle, bağlamında anahtar kelime hakkında net bir anlayışa scope
ve davranışa sahip olmanız gerekir .this
scope
this
& scope
:
there are two types of scope in javascript. They are :
1) Global Scope
2) Function Scope
kısaca global kapsam pencere nesnesini ifade eder, global kapsamda bildirilen değişkenlere her yerden erişilebilir, diğer yandan işlev kapsamı bir işlevin içinde bulunur. bir işlev içinde belirtilen değişken normal olarak dış dünyadan erişilemez. this
genel kapsamdaki anahtar kelime, pencere nesnesini ifade eder. this
inside işlevi aynı zamanda pencere nesnesine de atıfta bulunur.Bu this
nedenle this
, kendi seçtiğimiz bir bağlamı belirtmek için manipüle etmenin bir yolunu bulana kadar her zaman pencereye başvuracağız .
--------------------------------------------------------------------------------
- -
- Global Scope -
- ( globally "this" refers to window object) -
- -
- function outer_function(callback){ -
- -
- // outer function scope -
- // inside outer function"this" keyword refers to window object - -
- callback() // "this" inside callback also refers window object -
- } -
- -
- function callback_function(){ -
- -
- // function to be passed as callback -
- -
- // here "THIS" refers to window object also -
- -
- } -
- -
- outer_function(callback_function) -
- // invoke with callback -
--------------------------------------------------------------------------------
this
Geri arama işlevlerinin içinde işlem yapmanın farklı yolları :
Burada Kişi adında bir yapıcı işlevim var. Bu adlandırılan özelliği vardır name
ve adı verilen dört yöntem sayNameVersion1
, sayNameVersion2
, sayNameVersion3
, sayNameVersion4
. Dördünün de belirli bir görevi vardır.Bir geri aramayı kabul edin ve çağırın. Geri aramanın, Person yapıcı işlevinin bir örneğinin name özelliğini günlüğe kaydetmek olan belirli bir görevi vardır.
function Person(name){
this.name = name
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
this.sayNameVersion3 = function(callback){
callback.call(this)
}
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
}
function niceCallback(){
// function to be used as callback
var parentObject = this
console.log(parentObject)
}
Şimdi kişi yapıcısından bir örnek oluşturalım ve örneğe başvurmak için içerideki geri çağrıyı kaç yoldan değiştirebileceğimizi görmek sayNameVersionX
için (X, 1,2,3,4) yönteminin farklı sürümlerini çağıralım .niceCallback
this
person
var p1 = new Person('zami') // create an instance of Person constructor
Bağlamanın yapılması gereken, this
anahtar kelime sağlanan değere ayarlanmış yeni bir işlev oluşturmaktır .
sayNameVersion1
ve geri arama işlevini sayNameVersion2
işlemek için bind kullanın this
.
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
birincisi this
, yöntemin kendi içinde geri arama ile bağlanır ve ikincisi için, ona bağlı nesne ile geri arama iletilir.
p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method
p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback
first argument
Bir call
yöntem olarak kullanılmaktadır this
çağrılır işlevi içinde call
, kendisine bağlı.
sayNameVersion3
pencere nesnesi yerine oluşturduğumuz kişi nesnesine başvurmak call
için işlemek için kullanır this
.
this.sayNameVersion3 = function(callback){
callback.call(this)
}
ve şu şekilde adlandırılır:
p1.sayNameVersion3(niceCallback)
Benzer şekilde call
, ilk argümanı anahtar sözcükle apply
gösterilecek nesneyi ifade eder this
.
sayNameVersion4
kişi nesnesine başvurmak apply
için manipüle this
etmek için kullanır
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
ve aşağıdaki gibi çağrılır. Basitçe geri arama geçilir,
p1.sayNameVersion4(niceCallback)
Her setTimeout()
zaman global nesne (Pencere) ilethis
yürütüldüğü için, geri çağırma işlevindeki bağlama erişmek istiyorsanız , geri arama işlevini kullanarak bind()
şu şekilde başarabileceğimizi bağlayamayız:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
Soru, this
anahtar kelimenin javascript'te nasıl davrandığı etrafında döner . this
aşağıdaki gibi farklı davranır,
- Değeri
this
genellikle bir işlev yürütme bağlamı tarafından belirlenir. - Global kapsamda,
this
global nesneyi (window
nesne) ifade eder . - Katı mod herhangi bir işlev için etkinse sonra değeri
this
olacaktırundefined
Katı modda olduğu gibi, küresel nesne anlamına gelirundefined
yerinewindow
nesne. - Noktanın önünde duran nesne, bu anahtar kelimenin bağlı olacağı şeydir.
- Biz birlikte bunu açıkça değerini ayarlayabilirsiniz
call()
,bind()
veapply()
- Ne zaman
new
anahtar kelime (bir yapıcı) kullanılır, bu yeni nesne oluşturulan bağlıdır. - Ok İşlevleri bağlanmaz
this
- bunun yerinethis
sözcüksel olarak bağlanır (yani orijinal bağlama göre)
Yanıtların çoğunun önerdiği gibi, Ok işlevini veya bind()
Yöntem veya Öz var. Google JavaScript Stil Kılavuzu'ndan lambdalar (Ok işlevi) hakkında bir alıntı yapacağım
Ok işlevlerini f.bind (this) yerine ve özellikle goog.bind (f, this) yerine kullanmayı tercih edin. Const self = this yazmaktan kaçının. Ok işlevleri, bazen beklenmedik şekilde ek bağımsız değişkenler ileten geri aramalar için özellikle yararlıdır.
Google, bağlamak yerine lambdaların kullanılmasını açıkça önerir veya const self = this
Bu yüzden en iyi çözüm lambdaları aşağıdaki gibi kullanmak olacaktır,
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
Referanslar:
Şu anda, kodda sınıflar kullanılıyorsa başka bir yaklaşım mümkündür.
Sınıf alanlarının desteğiyle bunu bir sonraki yolla yapmak mümkündür:
class someView {
onSomeInputKeyUp = (event) => {
console.log(this); // this refers to correct value
// ....
someInitMethod() {
//...
someInput.addEventListener('input', this.onSomeInputKeyUp)
Elbette, kaputun altında, bağlamı bağlayan tüm eski iyi ok işlevi vardır, ancak bu formda, bu açık bağlama çok daha açık görünür.
Aşama 3 Teklifi olduğundan , şimdilik olduğu gibi işlemek için babel ve uygun babel eklentisine ihtiyacınız olacak (08/2018).
DOM2'ninthis
olay dinleyicisi içinde bağlanmasından bu yana standart yol olan, dinleyiciyi her zaman kaldırmanıza izin veren (diğer faydaların yanı sıra) başka bir yaklaşım, arayüzden handleEvent(evt)
yöntemdir EventListener
:
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
Kullanmayla ilgili ayrıntılı bilgi handleEvent
burada bulunabilir: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
this
JS'de:
this
JS'deki değeri % 100 bir işlevin nasıl tanımlandığına değil, nasıl çağrıldığına göre belirlenir. Biz nispeten kolayca değerini bulabilirsiniz this
tarafından 'nokta kuralının sol' :
- İşlev, function anahtar sözcüğü kullanılarak oluşturulduğunda, değeri
this
, çağrılan işlevin noktasının solundaki nesnedir. - Noktanın solunda nesne yoksa,
this
bir işlevin içindeki değer genellikle genel nesnedir (global
düğümde,window
tarayıcıda).this
Anahtar kelimeyi burada kullanmanızı tavsiye etmem çünkü böyle bir şey kullanmaktan daha az açık ve nettirwindow
! Function.prototype.bind()
Değerini sabitleyebilen bir işlev kullanılarak oluşturulan ok işlevleri ve işlevler gibi belirli yapılar vardırthis
. Bunlar kuralın istisnalarıdır ancak değerini düzeltmek için gerçekten faydalıdırthis
.
NodeJS'deki örnek
module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);
const obj1 = {
data: "obj1 data",
met1: function () {
console.log(this.data);
},
met2: () => {
console.log(this.data);
},
};
const obj2 = {
data: "obj2 data",
test1: function () {
console.log(this.data);
},
test2: function () {
console.log(this.data);
}.bind(obj1),
test3: obj1.met1,
test4: obj1.met2,
};
obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);
Çıktı:
Çıktıları 1'e 1 boyunca göstermeme izin verin (ikinciden başlayarak ilk günlüğü yok sayarak):
this
olduğuobj2
, çünkü nokta kuralı solun, biz nasıl görebilirsiniztest1
denirobj2.test1();
.obj2
noktanın solunda ve dolayısıylathis
değerdir.- Olsa
obj2
Noktanın bırakılır,test2
bağlı olduğuobj1
aracılığıbind()
yöntemle. Yanithis
değerobj1
. obj2
: adı verilen işlevin noktasının solundadırobj2.test3()
. Bu nedenleobj2
değeri olacaktırthis
.- Bu durumda:
obj2.test4()
obj2
noktanın soludur. Ancak, ok işlevlerinin kendithis
bağlantıları yoktur. Bu nedenle , başlangıçta günlüğe kaydedilen bir nesne olanthis
dış kapsamın değerine bağlanacaktırmodule.exports
. - Fonksiyonu
this
kullanarak değerini de belirtebilirizcall
. Buradathis
argüman olarak istenen değeri iletebilirizobj2
, bu durumda budur.
Ben sorunu bakıyordu Ngx
çizgi grafiği xAxisTickFormatting
HTML'den böyle denirdi fonksiyonu: [xAxisTickFormatting]="xFormat"
. Bileşenimin değişkenine bildirilen işlevden erişemedim. Bu çözüm, doğru olanı bulmak için sorunu çözmeme yardımcı oldu. Umarım bu Ngx
çizgi grafiğe, kullanıcılara yardımcı olur .
işlevi şu şekilde kullanmak yerine:
xFormat (value): string {
return value.toString() + this.oneComponentVariable; //gives wrong result
}
Bunu kullan:
xFormat = (value) => {
// console.log(this);
// now you have access to your component variables
return value + this.oneComponentVariable
}