JavaScript: Bunun anlamı nedir?

JavaScript'te this değerini bulmak zor olabilir. Bunu nasıl yapacağınız aşağıda açıklanmıştır.

Jake Archibald
Jake Archibald

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.

  1. Fonksiyon ok işlevi olarak tanımlanmışsa
  2. Aksi takdirde, işlev/sınıf new ile çağrılırsa
  3. Aksi takdirde, işlevin "bağlı" bir this değeri varsa
  4. Aksi takdirde, this çağrı zamanında ayarlanmışsa
  5. Aksi takdirde, işlev bir üst nesne (parent.func()) aracılığıyla çağrılırsa
  6. Aksi takdirde, işlev veya üst kapsam katı moddaysa
  7. 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:

Yapılmaması gerekenler:
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:

Yapılması gerekenler
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.