JavaScript’te Yineleyici Tasarım Modelinin Gücü – JSManifest

JavaScript'te Yineleyici Tasarım Modelinin Gücü – JSManifest

Yazdığımız hemen hemen her JavaScript uygulamasında her zaman koleksiyonlar arasında bir tür döngü vardır. Örneğin, bir http isteği yoluyla veri aldığımızda döngüsel koleksiyonlar gerekli hale gelir.

Veriler birçok form alabildiğinden, üzerlerinde döngü yapan her algoritma, işi diğer döngü yöntemlerinden daha hızlı ve/veya verimli bir şekilde yapamaz.

Bu durumda bariz bir sorun var: Farklı algoritmalar arasında geçiş yapabilmemiz gerekiyor. yineleme İhtiyacımız olduğunda koleksiyonlar aracılığıyla. Bu özel sorunu çözen bir tasarım deseni, Yineleyici Tasarım Modeli.

Bununla birlikte, bu yazıda JavaScript’te Yineleyici Tasarım Modelini gözden geçireceğiz ve bir uygulamalı uygulayacağız. Geliştirirken neden farklı bir algoritmanın gerekli olduğunu öğreneceğiz.

Yineleyici Modeli Görsel Olarak Nasıl Görünüyor?

Desen görsel açıdan şöyle görünür:

uygulama

Bir dizimiz olduğunu varsayalım arr hangi bir sayı koleksiyonudur:

const arr = [3, 10, 11, 11.1, 4, 0, 1, -1, 200, 108, 10, 0]

Bir program geliştiriyorsak ve bir değerler listesi alan yaygın olarak kullanılan bir fonksiyonumuz ve aradığımız değeri döndüren bir geri aramamız varsa, geri aramayı gerçeğin kaynağı olarak kullanarak, en bariz olanlardan biri. kullanabileceğimiz mevcut stratejiler döngü için:

function findValue(arr, callback) {
  for (let index = 0; index < arr.length; index++) {
    const value = arr[index]
    if (callback(value)) {
      return value
    }
  }
}

Veya Yineleyici Modelini kullanabilir ve bunun yerine bunları enjekte edilebilir algoritmalar olarak ele alarak farklı yineleme varyasyonlarıyla çalışabiliriz:

function createIterator(getIter) {
  return function getIterator() {
    let collection = this.collection
    let index = collection.length
    let iter = getIter(collection)

    return {
      next: iter.next,
      get index() {
        return 'index' in iter ? iter.index : index
      },
    }
  }
}

function makeTraverser() {
  return {
    traverse(collection) {
      this.collection = collection
      for (const value of this) console.log(value)
    },
    use(iter) {
      this[Symbol.iterator] = createIterator(iter)
      return this
    },
  }
}

Uygulama ayrıntılarını soyutlamanın daha iyi olmasının nedeni, koleksiyonların çeşitli veri yapılarında gelebilmesidir. Bazı veri yapıları, alternatif algoritmalarla daha verimli bir şekilde geçilebilir. Görevi bitirmek için daha yeterli algoritma olduğunda, nesnelerimizin çalışma zamanında bir yineleyiciden diğerine geçebilmesine izin verebiliriz.

Örneğimizle devam edelim ve basit bir yineleyici oluşturalım:

const traverser = makeTraverser()

traverser.use(function iterator(items) {
  return {
    next() {
      return {
        get value() {
          return items.pop()
        },
        get done() {
          return !items.length
        },
      }
    },
  }
})

Koleksiyonda dolaşabilir ve sonuçları görebiliriz:

const arr = [3, 10, 11, 11.1, 4, 0, 1, -1, 200, 108, 10, 0]
traverser.traverse(arr)

yineleyici-tasarım-kalıp-yineleyici-sonuç.png

Ya iç içe geçmiş bir eşya koleksiyonumuz olsaydı? Mevcut yineleme yöntemimiz artık verimli değil çünkü daha derindeki anahtar değerleri hesaba katmıyor:

const elems = [
  { tagName: 'div', style: { width: '28.5px', height: '20px' } },
  { tagName: 'label', style: { width: '28.5px', height: '20px' } },
  {
    tagName: 'div',
    style: { width: '28.5px', height: '20px' },
    children: [
      {
        tagName: 'div',
        style: { width: '28.5px', height: '20px' },
        children: [
          { tagName: 'input', style: { width: '28.5px', height: '20px' } },
          { tagName: 'input', style: { width: '28.5px', height: '20px' } },
          { tagName: 'select', style: { width: '28.5px', height: '20px' } },
          {
            tagName: 'div',
            style: { width: '28.5px', height: '20px' },
            children: [
              { tagName: 'input', style: { width: '28.5px', height: '20px' } },
              {
                tagName: 'div',
                style: { width: '28.5px', height: '20px' },
                children: [
                  {
                    tagName: 'a',
                    href: 'https://google.com',
                    target: '_blank',
                  },
                ],
              },
              { tagName: 'select', style: { width: '28.5px', height: '20px' } },
            ],
          },
          { tagName: 'input', style: { width: '28.5px', height: '20px' } },
        ],
      },
    ],
  },
]

traverser.traverse(elems)

yineleyici-tasarım-pattern-logging-elems.png

Veri koleksiyonları çok karmaşık hale gelebilir ve bunlar her zaman yalnızca düz JSON nesneleri değildir. JavaScript’teki programlar genellikle veri yapılarını getirir, dönüştürür ve yalnızca son bir veri koleksiyonu oluşturmak gibi ortak görevleri yapmak için birlikte çalışacak işlevlerle bunları dışarıdan herkese açık hale getirir.

Genişletilebilirliği, ölçeklenebilirliği ve kompozisyonu desteklediği için Yineleyici modelinin önemli olmasının nedeni budur.

Bununla birlikte, tüm torunlarımızı elde etmek için farklı bir algoritma kullanabileceğimizi söyledi. elems Toplamak:

traverser.use(function iterator(items) {
  items = [...items]

  const getItems = (_items) => {
    const nextItem = _items.pop()
    if (nextItem.children) _items.unshift(...nextItem.children)
    return nextItem
  }

  return {
    next() {
      return {
        get value() {
          return getItems(items)
        },
        get done() {
          return !items.length
        },
      }
    },
  }
})

Better-iterator-design-pattern-logging-elems-result.png

Eminim sözdizimini fark etmişsindir Symbol.iterator. JavaScript, bunu eklediğimiz herhangi bir nesne için varsayılan yineleyiciyi yerleştirmek için bunu kullanır. Kafanız karışmasın ve bu dır-dir desenin bir parçası. Bu sadece sözdizimi şekeri. etkinleştirmek için bu söz diziminden yararlanıyoruz. for of tamamen JavaScript ile ilgili olan bir işlem yapmak.

Doğruca Yineleyici Modeline geri dönelim. Yok Gerçekten Henüz ne kadar güçlü olabileceğini görmek için bu örneklerde gösterilen kullanışlı bir kullanım durumunun çoğu, ancak pratik bir kullanım durumunda çözdüğü bir sorunu inceledik.

Koleksiyonlarla nasıl çalışmak istediğimiz konusunda daha fazla kontrol eklemek için örneklerimizi daha da genişletelim çünkü sonraki bölümleri anlamak, bu kalıbın bize gerçekten nasıl yardımcı olduğunu anlamak için çok önemlidir.

Şimdiye kadar sahip olduğumuz, temsili DOM nesnelerinin iç içe geçmiş bir koleksiyonudur. Düzenli kullanırken yaşadığımız bir sorunu ortaya çıkardık for loop derin koleksiyonları kapsamanın yeterli olmadığı durumlarda, ihtiyaçlarımızı karşılamak için Yineleyici Modelinin bir varyasyonunu uyguladık.

Bununla birlikte, her yinelemede görebilme yeteneğine sahip olmak çok yardımcı olacaktır. şu anda neredeyiz karmaşık veri yapılarında

Mevcut öğeye (şu anda yaptığımız), mevcut öğeye erişmek istiyormuş gibi yapalım. derinlik biz varız (bir öğeye her erişim children artışlar depth), akım dizin koleksiyonun, koleksiyondaki önceki ve sonraki öğenin yanı sıra, koleksiyondaki önceki, şimdiki ve sonraki öğe yineleme (bu olumsuzluk geçerli koleksiyonla aynı) ve yinelemedeki öğelerin şu anda nasıl göründüğü.

Tüm bu bilgilerle, element koleksiyonumuzla istediğimiz hemen hemen her şeyi yapabiliriz.

Bunu şu şekilde uygulayabiliriz:

function createIterator(getIter) {
  return function getIterator() {
    let collection = this.collection
    let items = [...collection]
    let iter = getIter(collection)
    let depth = 0
    let index = 0
    let currentItem = null
    let currentElement = null
    let previousItem = null
    let previousElement = null
    let nextItem = null
    let nextElement = null

    const next = () => {
      previousItem = currentItem
      currentItem = collection[index]
      nextItem = collection[++index] || null

      if (currentElement?.children) {
        depth++
        items.unshift(...currentElement.children)
      }

      previousElement = currentElement
      currentElement = items.pop() || null
      nextElement = items[0] || null

      return {
        previousItem,
        previousElement,
        currentItem,
        currentElement,
        nextItem,
        nextElement,
        depth,
        index,
        items,
      }
    }

    return {
      get next() {
        return iter.next(next)
      },
    }
  }
}

function makeTraverser() {
  return {
    traverse(collection, callback) {
      this.collection = collection
      for (const value of this) callback(value)
    },
    use(iter) {
      this[Symbol.iterator] = createIterator(iter)
      return this
    },
  }
}

const traverser = makeTraverser()

traverser.use(function iterator(items) {
  return {
    next: (next) => () => {
      const props = next()
      return {
        get value() {
          return props
        },
        get done() {
          return !props.items.length
        },
      }
    },
  }
})

Şimdi tek yapmamız gereken onu çağırmak ve bir geri arama sağlamak. Geri aramamız, her yinelemede tüm bu bilgileri sağlayacaktır:

traverser.traverse(elems, function callback(args) {
  console.log(args)
})

Sonuçlar:

[
  {
    "previousItem": null,
    "previousElement": null,
    "currentItem": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "currentElement": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" },
      "children": [
        {
          "tagName": "div",
          "style": { "width": "28.5px", "height": "20px" },
          "children": [
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "select",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "div",
              "style": { "width": "28.5px", "height": "20px" },
              "children": [
                {
                  "tagName": "input",
                  "style": { "width": "28.5px", "height": "20px" }
                },
                {
                  "tagName": "div",
                  "style": { "width": "28.5px", "height": "20px" },
                  "children": [
                    {
                      "tagName": "a",
                      "href": "https://google.com",
                      "target": "_blank"
                    }
                  ]
                },
                {
                  "tagName": "select",
                  "style": { "width": "28.5px", "height": "20px" }
                }
              ]
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            }
          ]
        }
      ]
    },
    "nextItem": {
      "tagName": "label",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "nextElement": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "depth": 0,
    "index": 1,
    "items": []
  },
  {
    "previousItem": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "previousElement": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" },
      "children": [
        {
          "tagName": "div",
          "style": { "width": "28.5px", "height": "20px" },
          "children": [
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "select",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "div",
              "style": { "width": "28.5px", "height": "20px" },
              "children": [
                {
                  "tagName": "input",
                  "style": { "width": "28.5px", "height": "20px" }
                },
                {
                  "tagName": "div",
                  "style": { "width": "28.5px", "height": "20px" },
                  "children": [
                    {
                      "tagName": "a",
                      "href": "https://google.com",
                      "target": "_blank"
                    }
                  ]
                },
                {
                  "tagName": "select",
                  "style": { "width": "28.5px", "height": "20px" }
                }
              ]
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            }
          ]
        }
      ]
    },
    "currentItem": {
      "tagName": "label",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "currentElement": {
      "tagName": "label",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "nextItem": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" },
      "children": [
        {
          "tagName": "div",
          "style": { "width": "28.5px", "height": "20px" },
          "children": [
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "select",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "div",
              "style": { "width": "28.5px", "height": "20px" },
              "children": [
                {
                  "tagName": "input",
                  "style": { "width": "28.5px", "height": "20px" }
                },
                {
                  "tagName": "div",
                  "style": { "width": "28.5px", "height": "20px" },
                  "children": [
                    {
                      "tagName": "a",
                      "href": "https://google.com",
                      "target": "_blank"
                    }
                  ]
                },
                {
                  "tagName": "select",
                  "style": { "width": "28.5px", "height": "20px" }
                }
              ]
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            }
          ]
        }
      ]
    },
    "nextElement": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" },
      "children": [
        {
          "tagName": "input",
          "style": { "width": "28.5px", "height": "20px" }
        },
        {
          "tagName": "input",
          "style": { "width": "28.5px", "height": "20px" }
        },
        {
          "tagName": "select",
          "style": { "width": "28.5px", "height": "20px" }
        },
        {
          "tagName": "div",
          "style": { "width": "28.5px", "height": "20px" },
          "children": [
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "div",
              "style": { "width": "28.5px", "height": "20px" },
              "children": [
                {
                  "tagName": "a",
                  "href": "https://google.com",
                  "target": "_blank"
                }
              ]
            },
            {
              "tagName": "select",
              "style": { "width": "28.5px", "height": "20px" }
            }
          ]
        },
        {
          "tagName": "input",
          "style": { "width": "28.5px", "height": "20px" }
        }
      ]
    },
    "depth": 1,
    "index": 2,
    "items": []
  },
  {
    "previousItem": {
      "tagName": "label",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "previousElement": {
      "tagName": "label",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "currentItem": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" },
      "children": [
        {
          "tagName": "div",
          "style": { "width": "28.5px", "height": "20px" },
          "children": [
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "select",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "div",
              "style": { "width": "28.5px", "height": "20px" },
              "children": [
                {
                  "tagName": "input",
                  "style": { "width": "28.5px", "height": "20px" }
                },
                {
                  "tagName": "div",
                  "style": { "width": "28.5px", "height": "20px" },
                  "children": [
                    {
                      "tagName": "a",
                      "href": "https://google.com",
                      "target": "_blank"
                    }
                  ]
                },
                {
                  "tagName": "select",
                  "style": { "width": "28.5px", "height": "20px" }
                }
              ]
            },
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            }
          ]
        }
      ]
    },
    "currentElement": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" }
    },
    "nextItem": null,
    "nextElement": {
      "tagName": "div",
      "style": { "width": "28.5px", "height": "20px" },
      "children": [
        {
          "tagName": "input",
          "style": { "width": "28.5px", "height": "20px" }
        },
        {
          "tagName": "input",
          "style": { "width": "28.5px", "height": "20px" }
        },
        {
          "tagName": "select",
          "style": { "width": "28.5px", "height": "20px" }
        },
        {
          "tagName": "div",
          "style": { "width": "28.5px", "height": "20px" },
          "children": [
            {
              "tagName": "input",
              "style": { "width": "28.5px", "height": "20px" }
            },
            {
              "tagName": "div",
              "style": { "width": "28.5px", "height": "20px" },
              "children": [
                {
                  "tagName": "a",
                  "href": "https://google.com",
                  "target": "_blank"
                }
              ]
            },
            {
              "tagName": "select",
              "style": { "width": "28.5px", "height": "20px" }
            }
          ]
        },
        {
          "tagName": "input",
          "style": { "width": "28.5px", "height": "20px" }
        }
      ]
    },
    "depth": 1,
    "index": 3,
    "items": []
  }
]

Çözüm

Ve bu yazının sonu burada bitiyor! Umarım burada değerli bilgiler bulmuşsunuzdur ve gelecekte benden daha fazlasını beklersiniz!

Bir cevap yazın

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