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 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.

  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ş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:

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 kullanmı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 öğ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.