Temsilci Prototiplerini Anlamanız İçin 2 Neden – JSManifest

Temsilci Prototiplerini Anlamanız İçin 2 Neden – JSManifest

JavaScript ile ilgili bir kitapta bir bölüm okuyordum ve özellikle JavaScript’e yeni başlayanlar için yazmak istediğim bir sorunla (aynı zamanda sorunun kaynaklandığı kavramın gücüyle) karşılaştım. Ve yeni olmasanız bile, JavaScript’te bu sorun hakkında bilgi sahibi olmama ihtimaliniz var.

Bu makale, delege prototipleriyle bilinen bir anti-kalıp üzerinden geçecektir. React kullanıcıları için bu anti-desen kavramı onlara daha tanıdık gelebilir. Ancak, bugün JavaScript kitaplıklarının çoğunda kullanıldığını görebileceğiniz gibi, bu kavramı işleri tersine çevirmek ve uygulamalarınızın performansını büyük ölçüde artırmak için nasıl kullanabileceğinizi de gözden geçireceğiz!

Bu nedenle, JavaScript’te bir kitaplık oluşturmak istiyorsanız veya herhangi bir planınız varsa, uygulamanızın performansını artırmak için prototipleri devretmekten nasıl yararlanabileceğinizi anlayarak uygulamanızı nasıl optimize edebileceğinizi anlamanızı şiddetle tavsiye ederim. henüz onları anladı. diye bir isim var Flyweight Modeli bu makalede açıklanacaktır.

Bir prototipin ne olduğunu bilmiyorsanız, tüm prototipler temel olarak JavaScript’in diğer nesneleri modellemek için kullandığı nesnelerdir. Birden çok nesne örneği oluşturabilmesi açısından sınıflara benzer olduğunu söyleyebilirsiniz, ancak ayrıca bir nesnenin kendisi.

JavaScript’te, tüm nesnelerin bir temsilci prototipine bazı dahili referansları vardır. Nesneler özellik veya yöntem aramalarıyla sorgulandığında, JavaScript önce mevcut nesneyi kontrol eder ve eğer bu mevcut değilse, daha sonra nesnenin prototipini kontrol etmeye devam eder. temsilci prototipidir, ve sonra bu prototipin prototipi ile devam eder, vb. Prototip zincirinin sonuna ulaştığında, son durak kökte biter. Object prototip. Nesneler oluşturmak bu kökü ekler Object kök düzeyinde prototip. Object.create() ile ayarlanmış farklı anlık prototiplere sahip nesneleri dallara ayırabilirsiniz.

Aşağıdaki kod parçasına bir göz atalım:

const makeSorceress = function(type) {
  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    castThunderstorm(target) {
      target.hp -= 90
    },
  }
}

const makeWarrior = function(type) {
  let battleCryInterval

  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    bash(target) {
      target.hp -= 10
      this.lastTargets.names.push(target.name)
    },
    battleCry() {
      this.hp += 60
      battleCryInterval = setInterval(() => {
        this.hp -= 1
      }, 1000)
      setTimeout(() => {
        if (battleCryInterval) {
          clearInterval(battleCryInterval)
        }
      }, 60000)
      return this
    },
    lastTargets: {
      names: [],
    },
  }
}

const knightWarrior = makeWarrior('knight')
const fireSorc = makeSorceress('fire')

const bob = Object.create(knightWarrior)
const joe = Object.create(knightWarrior)
const lucy = Object.create(fireSorc)

bob.setName('bob')
joe.setName('joe')
lucy.setName('lucy')

bob.bash(lucy)

İki fabrika fonksiyonumuz var, bunlardan biri makeSorceress hangisi alır type bir argüman olarak büyücünün ve büyücünün yeteneklerinin bir nesnesini döndürür. Diğer fabrika işlevi makeWarrior hangisi alır type savaşçının bir argüman olarak ve savaşçının yeteneklerinin bir nesnesini döndürür.

type ile savaşçı sınıfının yeni bir örneğini başlatıyoruz knight tipi olan bir büyücü ile birlikte fire.

sonra kullandık Object.create bob, joe ve lucy için yeni nesneler yaratmak ve ayrıca her biri için prototip nesneleri delege etmek.

Bob, joe ve lucy, kendi mülklerini talep etmemiz ve beklememiz için örnekte isimleriyle belirlendi. Ve son olarak, bob kullanarak Lucy’ye saldırır. bashHP’sini 10 puan azaltıyor.

İlk bakışta, bu örnekte yanlış bir şey yok gibi görünüyor. Ama aslında bir sorun var. Bob ve joe’nun kendi özellik ve metot kopyalarına sahip olmasını bekliyoruz, bu yüzden kullandık Object.create. Bob, Lucy’yi dövdüğünde ve son hedeflenen adı this.lastTargets.names dizi, dizi yeni hedefin adını içerecektir.

Bunu kapatabilir ve kendimiz görebiliriz:

console.log(bob.lastTargets.names)

Davranış bekleniyor, ancak ayrıca son hedeflenen isimleri günlüğe kaydet için joeşunu görüyoruz:

console.log(joe.lastTargets.names)

Bu mantıklı değil, değil mi? Lucy’ye saldıran kişi, yukarıda açıkça gösterildiği gibi Bob’du. Ama neden görünüşe göre Joe olaya dahil oldu? Bir kod satırı açıkça yazar bob.bash(lucy)ve bu kadar.

Sorun şu ki, Bob ve Joe aslında aynı durum!

Ama bekleyin, bu hiç mantıklı değil çünkü kullandığımızda kendi ayrı kopyalarını yaratmalıydık. Object.createya da biz öyle varsaydık.

Hatta MDN’deki docs açıkça Object.create() yönteminin bir yeni nesne. Yeni bir nesne yaratır – ki yaptı, ama buradaki sorun, eğer nesne veya dizi özelliklerini değiştirirseniz, prototip özellikleri, mutasyon sızıntı ve prototip zincirinde bu prototiple bir bağlantısı olan diğer örnekleri etkiler. bunun yerine değiştirirseniz bütün prototip üzerindeki özellik, değişiklik sadece örneğinde gerçekleşir.

Örneğin:

const makeSorceress = function(type) {
  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    castThunderstorm(target) {
      target.hp -= 90
    },
  }
}

const makeWarrior = function(type) {
  let battleCryInterval

  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    bash(target) {
      target.hp -= 10
      this.lastTargets.names.push(target.name)
    },
    battleCry() {
      this.hp += 60
      battleCryInterval = setInterval(() => {
        this.hp -= 1
      }, 1000)
      setTimeout(() => {
        if (battleCryInterval) {
          clearInterval(battleCryInterval)
        }
      }, 60000)
      return this
    },
    lastTargets: {
      names: [],
    },
  }
}

const knightWarrior = makeWarrior('knight')
const fireSorc = makeSorceress('fire')

const bob = Object.create(knightWarrior)
const joe = Object.create(knightWarrior)
const lucy = Object.create(fireSorc)

bob.setName('bob')
joe.setName('joe')
lucy.setName('lucy')

bob.bash(lucy)
bob.lastTargets = {
  names: [],
}

console.log(bob.lastTargets.names) 
console.log(joe.lastTargets.names) 

değiştirirseniz this.lastTargets.names özelliği, prototipe bağlı diğer nesnelerle yansıtılacaktır. Ancak, prototipin özelliğini değiştirdiğinizde (this.lastTargets), bu özelliği geçersiz kılar sadece o örnek için. Yeni bir geliştiricinin bakış açısına göre, bunu kavramak biraz zor olabilir.

React kullanarak düzenli olarak uygulama geliştiren bazılarımız, uygulamalarımızda durumu yönetirken bu sorunla yaygın olarak ilgilendik. Ancak muhtemelen hiç dikkat etmediğimiz şey, bu kavramın JavaScript dilinin kendisinden nasıl kaynaklandığıdır. Buna daha resmi bir şekilde bakmak için, JavaScript dilinin kendi içinde bir sorun, bunun bir anti model olması.

Ama neden bu bile bir anti model? İyi bir şey olamaz mı?

Bunu belirli şekillerde Yapabilmek iyi bir şey çünkü bellek kaynaklarını korumak için yöntemler atayarak uygulamalarınızı optimize edebilirsiniz. Sonuçta, her nesnenin sadece ihtiyacı var Bir Kopya ve yöntemler, bu örneğin ek işlevsellik için geçersiz kılması gerekmedikçe, tüm örnekler boyunca paylaşılabilir.

Örneğin, geçmişe bakalım makeWarrior işlev:

const makeWarrior = function(type) {
  let battleCryInterval

  return {
    type: type,
    hp: 100,
    setName(name) {
      this.name = name
    },
    name: '',
    bash(target) {
      target.hp -= 10
      this.lastTargets.names.push(target.name)
    },
    battleCry() {
      this.hp += 60
      battleCryInterval = setInterval(() => {
        this.hp -= 1
      }, 1000)
      setTimeout(() => {
        if (battleCryInterval) {
          clearInterval(battleCryInterval)
        }
      }, 60000)
      return this
    },
    lastTargets: {
      names: [],
    },
  }
}

bu battleCry işlevinin doğru çalışması için herhangi bir koşula bağlı olmadığından, bunun yanı sıra bir hp örnekleme üzerine zaten ayarlanmış olan özellik. Bu işlevin yeni oluşturulan örnekleri, mutlaka kendi kopyalarına ihtiyaç duymaz. battleCry ve bunun yerine başlangıçta bu yöntemi tanımlayan prototip nesnesine yetki verebilir.

Aynı prototipin örnekleri arasında veri paylaşımının anti modeli, depolama durumunun en büyük dezavantaj olmasıdır, çünkü paylaşılan özellikleri veya mutasyona uğramaması gereken verileri yanlışlıkla değiştirmek çok kolay hale gelebilir, ki bu uzun zamandır yaygın bir hata kaynağı olmuştur. JavaScript uygulamaları için.

Ne kadar popüler olduğuna bakarsak, bu uygulamanın aslında iyi bir nedenle kullanıldığını görebiliriz. rica etmek paketi başlatır Har işlev bu kaynak kodu:

function Har(request) {
  this.request = request
}

Har.prototype.reducer = function(obj, pair) {
  
  if (obj[pair.name] === undefined) {
    obj[pair.name] = pair.value
    return obj
  }

  
  var arr = [obj[pair.name], pair.value]

  obj[pair.name] = arr

  return obj
}

peki neden olmuyor Har.prototype.reducer sadece bu şekilde tanımlansın mı?

function Har(request) {
  this.request = request

  this.reducer = function(obj, pair) {
    
    if (obj[pair.name] === undefined) {
      obj[pair.name] = pair.value
      return obj
    }

    
    var arr = [obj[pair.name], pair.value]

    obj[pair.name] = arr

    return obj
  }
}

Daha önce açıklandığı gibi, daha yeni örnekler başlatılacak olsaydı, uygulamalarınızın performansını gerçekten düşürürdü çünkü [recreating new methods on each instantiation]hangisi reducer işlev.

Ayrı örneklerimiz olduğunda Har:

const har1 = new Har(new Request())
const har2 = new Har(new Request())
const har3 = new Har(new Request())
const har4 = new Har(new Request())
const har5 = new Har(new Request())

aslında biz yaratıyoruz 5 ayrı nüsha this.reducer bellekte çünkü yöntem örnek düzeyinde tanımlanmıştır. Redüktör doğrudan prototip üzerinde tanımlanmışsa, birden çok örnek Har niyet temsilci en reducer prototipte tanımlanan yönteme göre işlev! Bu, temsilci prototiplerinden nasıl yararlanabileceğinize ve uygulamalarınızın performansını nasıl iyileştireceğinize bir örnektir.

Çözüm

Tüm söylemem gereken buydu. Umarım bu yazıdan bir şeyler öğrenmişsinizdir ve bir dahaki sefere görüşürüz!

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.