Почему array.map вложен в array.forEach намного быстрее (в данном случае), чем наоборот?

#javascript #performance

Вопрос:

У меня есть страница, которая дает пользователю возможность фильтровать местоположения магазинов, нажимая на ссылки для региона и отдельных магазинов, вложенных в эти регионы. Каждый щелчок обновляет объект, содержащий примененные фильтры, скрывает все элементы, а затем показывает только отфильтрованный набор элементов.

Мой код в этом посте берет данные из объекта filters и создает строку селектора CSS, к которой .show() может быть применен метод jQuery.

Я попробовал сделать это 2 разными способами, думая, что в производительности не будет большой разницы, но, к моему удивлению, jsbench.me показал, что решение № 2 (ниже) на ~90% медленнее, и я не понимаю, почему.

Вот код, который создает строку селектора CSS для каждого решения. Они оба используют один и тот же объект, представляющий текущие активные фильтры. Их также можно найти в этом коде (переменные называются немного по-другому, но код такой же, как и ниже).

Пример Объекта Активные Фильтры

 const filts = {
  levels: [],
  locations: {
    "new-york": ["monore", "chester", "cortalnd"],
    colorado: ["denver"],
    penn: [],
  }
};
 

Решение 1:

 const locs2 = filts.locations;
  const activeRegions = Object.keys(locs2);
  if(activeRegions.length) {        
    let sels2 = [];       
    activeRegions.forEach(region => {          
      const regionSelStr = `#region-holder__${region}`;
      sels2.push(regionSelStr);         
      const shops2 = locs2[region];
      if(shops2.length) {            
        const shopsSels = shops2.map(shop => `#shop-holder_${region}_${shop}`);
        sels2.push(...shopsSels);               
      } else {
        sels2.push(`${regionSelStr} .shop-holder`);
      }         
    });  
    console.log(sels2.join(','));
  }
 

Решение 2:

   const locs = filts.locations;
  const regions = Object.keys(locs);
  const sels = regions.map(region => {
    const regionPart = `#region-holder__${region}`;
    let str = `${regionPart},`;
    const shopPart = locs[region];
    if(!shopPart.length) {
      str  = `${regionPart} .shop-holder`;
    } else {
      shopPart.forEach((shop, i, arr) => { arr[i] = `#shop-holder_${region}_${shop}` });
      str  = shopPart.join(',');
    }
    return str;
  });
  console.log(sels.join(','));
 

Каждый из них использует a .forEach() и a .map() для итерации, но в каком порядке они используются/вложены? Или это потому, что решение 2 изменяет значение filts объекта, когда это Object.keys(filts.locations).map(...) происходит ? Есть какие-нибудь идеи?

Редактировать

Я изменил решение 2, чтобы использовать интерполяцию строк (сначала по недосмотру с моей стороны) — все еще на ~ 88% медленнее в соответствии с jsbench.me

ПРАВКА 2

Я изменил решение 2, чтобы НЕ вызывать array.join его на каждой итерации, а просто начать создавать строку прямо в forEach() , и теперь она быстрее примерно на 10%

   const locs = filts.locations;
  const regions = Object.keys(locs);
  const sels = regions.map(region => {
    const regionPart = `#region-holder__${region}`;
    let str = `${regionPart},`;
    const shopPart = locs[region];
    if(!shopPart.length) {
      str  = `${regionPart} .shop-holder`;
    } else {
      shopPart.forEach((shop, i, arr) => { 
        str  = `#shop-holder_${region}_${shop},`; 
      });
    }
    return str;
  });
  console.log(sels.join(','));
 

Комментарии:

1. Вы также проверяете относительную скорость объединения строк по сравнению с интерполяцией строк и объединениями массивов. Если вы хотите знать разницу в производительности только map и forEach , оставьте все остальное точно таким же.

2. вы можете проверить производительность своего кода здесь : jsben.ch

3. @HannaRose операция уже использовала инструмент сравнительного анализа; это в вопросе.

4. Решение @HereticMonkey — oops также должно было использовать интерполяцию строк. Я обновлю и протестирую еще раз. Будет интересно посмотреть, не закроет ли это немного пробел

Ответ №1:

Разница между forEach и map на самом деле не будет существенной, forEach скорее всего, будет иметь небольшое преимущество, так как для этого не нужно создавать новый массив, но это всего лишь предположение. Причина, почему ваш код работает медленнее в раствор 2 С forEach происходит потому, что 1. вы используете join которая выполнит объединения массива на каждой итерации (по сути-создание факториал петли), а также 2. вы используете конкатенацию строк, которая будет медленнее, чем интерполяция, как прокомментировать ваш вопрос уже упоминали. Решение 1 просто линейно с небольшим добавлением дополнительных элементов для добавления новых элементов в массив, что незначительно.

Комментарии:

1. Это определенно было то самое array.join() … см. Изменения в OP