Как избавиться от зацикливания кода при использовании вложенных функций

Друзья, и снова нужна помощь знающих людей. Есть у меня простенькая функция для построения таблиц и функция для заполнения этой самой таблицы:

function createTableEmpty(id, colRows, colColums) {
  let table = document.getElementById(id);
  let caption = document.createElement('caption')
  caption.id = "caption"
  caption.className = "text2"

  for (let i = 0; i < colRows; i++) {
    let tr = document.createElement('tr');

      for (let j = 0; j < colColums; j++) {
        let td = document.createElement('td');
        td.id = `tc${i}${j}`
        if (i == 0) {
          td.className = "cell4 cell"
        } else {
          td.className = "cell4"
        }
        tr.appendChild(td);
      }

    table.appendChild(caption);
    table.appendChild(tr);
  }
}


function printDataResTest(data) {
  let j = 0;
  let property = Object.getOwnPropertyNames(data)
  for (var i = 0; i < property.length; i++) {
    if (Array.isArray(data[property[i]])) {
      for (var k = 0; k < data.rows.length; k++) {
        document.getElementById(`tc${k}${j}`).innerHTML = data[property[i]][k]
      }
      j++
    }
  }
};

функция построения таблицы принимает в себя "место построения" в хтмл, а так же количество столбцов и строк будущей таблицы, которые вычисляются на основе количества массивов в базовой переменной resName, которая является обьектом, (количество столбцов) и длины этих массивов (количество строк).

После того, как таблица построена, она начинает заполняться содержимым массивов из переменной resName.

Сама переменная выглядит следующим образом:

const HSS = HERO.sexStats;

  let resName = {
      name: ['Act', 'Virginity', 'Pregnant', 'Birth', 'Fertilization', 'Vaginal sex', 'Anal sex', 'Oral sex', 'Piss drinking', 'Rimjob', 'Footjob', 'Breastjob'],

      get data1() {
          let a = [];

            a[0] = 'Data'
          if (HSS.virginity == 0) {
            a[1] = 'Yes'
          } else {
            a[1] = 'No'
          }
          if (HERO.genderStats.pregnancy == 1) {
            a[2] = 'Yes'
          } else {
            a[2] = 'No'
          }
          if (HERO.genderStats.birth != 0) {
            a[3] = HERO.genderStats.birth
          } else {
            a[3] = 'No'
          }
          if (HERO.genderStats.inseminate == 'Yes') {
            a[4] = 'Yes'
          } else {
            a[4] = 'No'
          }
          for (var i = 0; i < this.support.dom.length; i++) {
            a[i+5] = this.support.dom[i] + this.support.sub[i];
          }
      return a;
    },

      get data2() {
      let j = 0
      let a = []
      let b = ['Virginity', 'Pregnant', 'Birth', 'Fertilization']
      for (var i = 0; i < this.name.length; i++) {
        if (this.name[i] == 'Act') {
          a[i] = 'Details'
        } else {
            if (b.includes(this.name[i])) {
              a[i] = '';
            } else {
              a[i] = `Dom: ${this.support.dom[j]} </br> Sub: ${this.support.sub[j]}`
              j++
            }
          }
      }
      if (this.data1[4] == 'Yes') {
        a[4] = HSS.sexFertilization
      } else {
        a[4] = '';
      }
      return  a;
      },

      get rows() {
        let a = this.name.length;
        return a;
      },
      get colums() {
        let a = 0
        let property = Object.getOwnPropertyNames(this)
        console.log('property: ', property)
        for (var i = 0; i < property.length; i++) {
          if (Array.isArray(this[property[i]])) {
            a++
          }
        }
        return a;
      },
      get keys() {
           return objectKey(this)
         },
      support: {
        
        dom: [HSS.sexVaginalDom, HSS.sexAnalDom, HSS.sexOralDom, HSS.pissDrinkingDom, HSS.sexRimjobDom, HSS.sexFootjobDom, HSS.sexBreastjobDom,],
        sub: [HSS.sexVaginalSub, HSS.sexAnalSub, HSS.sexOralSub, HSS.pissDrinkingSub, HSS.sexRimjobSub, HSS.sexFootjobSub, HSS.sexBreastjobSub,],
        id: [
        ['tc00', 'tc01', 'tc02'],
        ['tc10', 'tc11', 'tc12'],
        ['tc20', 'tc21', 'tc22'],
        ['tc30', 'tc31', 'tc32'],
        ['tc40', 'tc41', 'tc42'],
        ['tc50', 'tc51', 'tc52'],
        ['tc60', 'tc61', 'tc62'],
        ['tc70', 'tc71', 'tc72'],
        ['tc80', 'tc81', 'tc82'],
        ['tc90', 'tc91', 'tc92'],
        ['tc100', 'tc101', 'tc102'],
        ['tc110', 'tc111', 'tc112'],
      ],
      },

    };

function objectKey(obj) {
  let keys = Object.keys(obj)
  return keys;
}

createTableEmpty('tableHeroStats', resName.rows, resName.colums);
printDataResTest(resName);

Как можно увидеть из кода, внутри нее много геттеров, которые производят вычисления сразу по ходу выполнения кода. Но вот тут-то и возникла проблема: каждый раз, когда функция построения таблицы и/или функция ее заполнения выполняет цикл, все геттеры начинают производить перерасчёты заново.

В результате, код зависает (или скорее выполняется крайне долго. Настолько долго, что компилятор просто прерывает данную процедуру, решив, что процесс бесконечен).

Итак, мой вопрос: можно ли как-то преобразовать геттеры, чтобы они не выполняли перерасчет при каждом новом шаге цикла функции построения таблицы и/или ее заполнения, а выполнялись лишь раз, при первом запуске, а после просто хранили полученное значение?

============================================================================

let resName = {
      name: ['Act', 'Virginity', 'Pregnant', 'Birth', 'Fertilization', 'Vaginal sex', 'Anal sex', 'Oral sex', 'Piss drinking', 'Rimjob', 'Footjob', 'Breastjob'],
      data1: [],
      data2: [],
      get creatData1() {
          let a = [];
          if (this.data1.length != 0) {
            return this.data1
          } else {
            a[0] = 'Data'
          if (HSS.virginity == 0) {
            a[1] = 'Yes'
          } else {
            a[1] = 'No'
          }
          if (HERO.genderStats.pregnancy == 1) {
            a[2] = 'Yes'
          } else {
            a[2] = 'No'
          }
          if (HERO.genderStats.birth != 0) {
            a[3] = HERO.genderStats.birth
          } else {
            a[3] = 'No'
          }
          if (HERO.genderStats.inseminate == 'Yes') {
            a[4] = 'Yes'
          } else {
            a[4] = 'No'
          }
          for (var i = 0; i < this.support.dom.length; i++) {
            a[i+5] = this.support.dom[i] + this.support.sub[i];
          }
      //return a;
      this.data1 = a
          }

    },

      get creatData2() {
      let j = 0
      let a = [];
      let b = ['Virginity', 'Pregnant', 'Birth', 'Fertilization']
      if (this.data2.length != 0) {
        return this.data2
      } else {
        for (var i = 0; i < this.name.length; i++) {
          if (this.name[i] == 'Act') {
            a[i] = 'Details'
          } else {
              if (b.includes(this.name[i])) {
                a[i] = '';
              } else {
                a[i] = `Dom: ${this.support.dom[j]} </br> Sub: ${this.support.sub[j]}`
                j++
              }
            }
        }
        if (this.data1[4] == 'Yes') {
          a[4] = HSS.sexFertilization
        } else {
          a[4] = '';
        }
        //return  a;
        this.data2 = a
      }

      },

      get rows() {
        let a = this.name.length;
        return a;
      },
      
      colums: 0,
      get sumColums() {
        let a = 0
        if (this.colums != 0) {
          return this.colums
        } else {
          let property = Object.keys(this)
          console.log('property: ', property)
          for (var i = 0; i < property.length; i++) {
            if (Array.isArray(this[property[i]])) {
              a++
            }
          }
          this.colums = a;
        }

      },

      support: {
        dom: [HSS.sexVaginalDom, HSS.sexAnalDom, HSS.sexOralDom, HSS.pissDrinkingDom, HSS.sexRimjobDom, HSS.sexFootjobDom, HSS.sexBreastjobDom,],
        sub: [HSS.sexVaginalSub, HSS.sexAnalSub, HSS.sexOralSub, HSS.pissDrinkingSub, HSS.sexRimjobSub, HSS.sexFootjobSub, HSS.sexBreastjobSub,],
        id: [
        ['tc00', 'tc01', 'tc02'],
        ['tc10', 'tc11', 'tc12'],
        ['tc20', 'tc21', 'tc22'],
        ['tc30', 'tc31', 'tc32'],
        ['tc40', 'tc41', 'tc42'],
        ['tc50', 'tc51', 'tc52'],
        ['tc60', 'tc61', 'tc62'],
        ['tc70', 'tc71', 'tc72'],
        ['tc80', 'tc81', 'tc82'],
        ['tc90', 'tc91', 'tc92'],
        ['tc100', 'tc101', 'tc102'],
        ['tc110', 'tc111', 'tc112'],
      ],
      },

    };

Ответы (2 шт):

Автор решения: Алексей Шиманский

можно ли как-то преобразовать геттеры, чтобы они не выполняли перерасчет при каждом новом шаге цикла функции построения таблицы и/или ее заполнения, а выполнялись лишь раз, при первом запуске, а после просто хранили полученное значение?

Конечно можно. По-сути вы сами ответили на свой же вопрос.


Задайте в объекте переменные с типом массив, которые изначально пустые. В геттерах вначале нужно сделать проверку, если эта внутренняя переменная не пуста - возврать данные оттуда, если пустые - то делать все манипуляции и заносить эти данные в переменные.

Вообще, если планируется только единожды заполнить данные и потом их использовать в геттерах, то, в таком случае, нужно сделать отдельные методы с логикой просчёта, которые вызовуться в конструкторе объекта. Там данные заскладировать во внутренние переменные объекта (как я написал выше), а геттер будет потом просто брать из них данные

→ Ссылка
Автор решения: Daniil Loban

Данный код, имеет 2 проблемы:

  • не правильное использование геттера rows
  • рекусия.

Помимо этого присутсвует избыточность и плохое форматирование кода (рекомендуется прочесть "Чистый Код")

Первая проблема выражена в том что:

get rows() {
  let a = this.name.length;
  return a;
},

по сути возвращает this.name.length что является однозначно числом поэтому использование в таком контексте:

for (var k = 0; k < data.rows.length; k++) {
  document.getElementById(`tc${k}${j}`).innerHTML = data[property[i]][k]
}

просто является неприемлимым (брать length у числа, не имеет ни какого смысла) вероятно это произошло из-за невнимательности к собственному коду.

Первая часть решения:

get rows() {
  return this.name;
},

Заводить переменную a в данном случае лишено смысла ибо мы возвращаем объект по ссылке в любом случае один и тот же (это один из примеров борьбы с избыточностью).

Вторая проблема выражена в том что:

get sumColums() {
  let a = 0
  if (this.colums != 0) {
    return this.colums
  } else {
    let property = Object.keys(this)
    console.log('property: ', property)
    for (var i = 0; i < property.length; i++) {
      if (Array.isArray(this[property[i]])) {
        a++
      }
    }
    this.colums = a;
  }
},

Данная функция в конкретно этом коде вызывает сама себя при попытке обращения к this[property[i]] где property[i] в какой-то момент цикла эквивалентно 'sumColums' таким образом исключив этот ключ геттера можно решить проблему с рекурсией:

Вторая часть решения:

get sumColums() {
  let a = 0
  if (this.colums != 0) {
    return this.colums
  } else {
    let property = Object.keys(this)
    console.log('property: ', property)
    for (var i = 0; i < property.length; i++) {
      if (property[i] === 'sumColums') continue // добавлено для пропуска
      if (Array.isArray(this[property[i]])) {
        a++
      }
    }
    this.colums = a;
  }
},
→ Ссылка