Выбор обхода цикла для производительности?

JavaScript

Пять сил, прибывающих на поле битвы:for , foreach , map , for...in , for...of

самопрезентация

for

Я первый принц в мире обхода, и все здесь должны называть меня дедушкой. Я могу удовлетворить подавляющее большинство потребностей разработчиков.

// 遍历数组
let arr = [1,2,3];
for(let i = 0;i < arr.length;i++){
    console.log(i)          // 索引,数组下标
    console.log(arr[i])     // 数组下标所对应的元素
}

// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i = 0, keys=Object.keys(profile); i < keys.length;i++){
    console.log(keys[i])            // 对象的键值
    console.log(profile[keys[i]])   // 对象的键对应的值
}

// 遍历字符串
let str = "abcdef";
for(let i = 0;i < str.length ;i++){
    console.log(i)          // 索引 字符串的下标
    console.log(str[i])     // 字符串下标所对应的元素
}

// 遍历DOM 节点
let articleParagraphs = document.querySelectorAll('.article > p');
for(let i = 0;i<articleParagraphs.length;i++){
    articleParagraphs[i].classList.add("paragraph");
    // 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}

forEach

Я публикую в версии ES5. Выполняет функцию обратного вызова один раз для каждого элемента в массиве, который имеет допустимое значение в порядке возрастания, эти удаленные или неинициализированные элементы будут пропущены (например, в разреженных массивах). Я улучшенная версия цикла for.

// 遍历数组
let arr = [1,2,3];
arr.forEach(i => console.log(i))

// logs 1
// logs 2
// logs 3
// 直接输出了数组的元素

//遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
let keys = Object.keys(profile);
keys.forEach(i => {
    console.log(i)              // 对象的键值
    console.log(profile[i])     // 对象的键对应的值
})

map

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

let arr = [1,2,3,4,5];
let res = arr.map(i => i * i);

console.log(res) // logs [1, 4, 9, 16, 25]

для... перечисление

Я публикую в версии ES5. Обходит перечислимые свойства объекта, кроме Symbol, в любом порядке.

// 遍历对象
let profile = {name:"April",nickname:"二十七刻",country:"China"};
for(let i in profile){
    let item = profile[i];
    console.log(item)   // 对象的键值
    console.log(i)      // 对象的键对应的值

// 遍历数组
let arr = ['a','b','c'];
for(let i in arr){
    let item = arr[i];
    console.log(item)   // 数组下标所对应的元素
    console.log(i)      // 索引,数组下标

// 遍历字符串
let str = "abcd"
for(let i in str){
    let item = str[i];
    console.log(item)   // 字符串下标所对应的元素
    console.log(i)      // 索引 字符串的下标
}

для...итерации

Я публикую в версии ES6. Создайте итерационный цикл для итерируемых объектов (включая Array, Map, Set, String, TypedArray, объекты arguments и т. д.), вызовите настраиваемые обработчики итерации и выполните операторы для значения каждого отдельного свойства.

// 迭代数组数组
let arr = ['a','b','c'];
for(let item of arr){
    console.log(item)     
}
// logs 'a'
// logs 'b'
// logs 'c'

// 迭代字符串
let str = "abc";
for (let value of str) {
    console.log(value);
}
// logs 'a'
// logs 'b'
// logs 'c'

// 迭代map
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]
for (let entry of iterable) {
    console.log(entry);
}
// logs ["a", 1]
// logs ["b", 2]
// logs ["c", 3]

//  迭代map获取键值
for (let [key, value] of iterable) {
    console.log(key)
    console.log(value);
}


// 迭代set
let iterable = new Set([1, 1, 2, 2, 3, 3,4]);
for (let value of iterable) {
    console.log(value);
}
// logs 1
// logs 2
// logs 3
// logs 4

// 迭代 DOM 节点
let articleParagraphs = document.querySelectorAll('.article > p');
for (let paragraph of articleParagraphs) {
    paragraph.classList.add("paragraph");
    // 给class名为“article”节点下的 p 标签添加一个名为“paragraph” class属性。
}

// 迭代arguments类数组对象
(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);
// logs:
// 1
// 2
// 3


// 迭代类型数组
let typeArr = new Uint8Array([0x00, 0xff]);
for (let value of typeArr) {
  console.log(value);
}
// logs:
// 0
// 255

После первого раунда представления себя и презентации навыков мы узнали:

  • for语句является исходным оператором цикла. определить переменнуюi(числовой тип, указывающий индекс массива), по определенным условиям, наiВыполняется круговое накопление. Условием обычно является длина объекта цикла, и цикл останавливается, когда длина превышена. Поскольку объект не может судить о длине, поэтому сопоставьтеObject.keys()использовать.
  • forEachПредлагается ES5. утверждая, чтоfor语句расширенная версия, можно обнаружить, что она болееfor语句Написано гораздо проще. Но это также, по сути, цикл по массиву.forEachФункция обратного вызова выполняется один раз для каждого элемента массива. То есть массив, для которого он вызывается, поэтому исходный массив не будет изменен. Возвращаемое значениеundefine.
  • mapПредлагается ES5. Функция обратного вызова вызывается последовательно для каждого элемента исходного массива. Создает новый массив без изменения самого исходного массива, вызвавшего его. Возвращаемое значение — новый массив.
  • for...inПредлагается ES5. Просматривайте перечисляемые свойства объекта, включая свойства объекта-прототипа, в любом порядке, т. е. порядок не является фиксированным. При обходе массива нижний индекс массива используется в качестве значения ключа, а i в это время является строкой. Он создан для перебора свойств объекта и не рекомендуется для использования с массивами.
  • for...ofПредлагается ES6. Перебирать только данные итерируемого.

Проверка способностей

Программисту недостаточно просто знать их и определять их сильные и слабые стороны в реальной разработке. Используйте их в соответствии с местными условиями и избегайте слабых мест. Таким образом, улучшение общей производительности программы - вот в чем заключается способность.

О выпрыгивании тела из петли

Если в цикле выполняется определенное условие, он выпрыгнет из тела цикла или пропустит данные, не соответствующие условиям, и продолжит цикл по другим данным. часто встречающееся требование. Обычно используемые предложенияbreakа такжеcontinue.

Просто скажите о разнице между ними, просто просмотрите ее.

  • breakОператор должен выйти из текущего цикла и выполнить оператор после текущего цикла;
  • continueОператор должен завершить текущий цикл и продолжить выполнение следующего цикла;

Уведомление:forEachа такжеmapОн не поддерживает прыжки из корпуса петли, а остальные три метода поддерживаются.

принцип:ПроверитьforEachПринцип реализации поймет проблему.

Array.prototype.forEach(callbackfn [,thisArg]{
    
}

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

Использование return в функции обратного вызова просто возвращает результат вышестоящей функции, то есть циклу for, и не завершает цикл for, поэтому return также недействителен.

map()То же самое справедливо.

map()цепной вызов

map()Метод является цепным, что означает, что его можно легко комбинировать с другими методами. Например:reduce(), sort(), filter()Ждать. Но другие методы этого не делают.forEach()Возвращаемое значениеundefined, поэтому его нельзя связать.

// 将元素乘以本身,再进行求和。
let arr = [1, 2, 3, 4, 5];
let res1 = arr.map(item => item * item).reduce((total, value) => total + value);

console.log(res1) // logs 55 undefined"

for...inОн будет проходить по свойствам объекта-прототипа.

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {
    console.log(i);
}
// logs
// 0
// 1
// 2
// foo
// arrCustom
// objCustom

Однако в реальной разработке нам не нужны свойства объекта-прототипа. В этом случае мы можем использоватьhasOwnProperty()Метод, который возвращает логическое значение, указывающее, имеет ли объект указанное свойство (то есть, есть ли у него указанный ключ) среди собственных свойств. следующим образом:

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
var arr = ['a', 'b', 'c'];
arr.foo = 'hello
for (var i in arr) {
    if (arr.hasOwnProperty(i)) {
        console.log(i);
    }
}
// logs
// 0
// 1
// 2
// foo

// 可见数组本身的属性还是无法摆脱。此时建议使用 forEach

Для чистого обхода объекта выберитеfor..inПеречисление удобнее, для обхода массива, если не нужно знать индексfor..ofИтерация более уместна, так как ее тоже можно прервать; если нужно знать индекс, тоforEach()более подходящим; для других строк, подобных массиву, итераций типизированных массивов,for..ofДаже лучше. Но обратите внимание на совместимость с более ранними версиями браузеров.

представление

Заинтересованные читатели могут найти набор данных самопроверки, в статье приведены непосредственно результаты и сделать соответствующие пояснения.

for > for-of > forEach  > map > for-in
  • forЦикл, конечно, самый простой, потому что он не имеет никакого дополнительного стека вызовов функций и контекста;
  • for...ofВы можете использовать его для перебора членов любой структуры данных, имеющей интерфейс Iterator. Он напрямую считывает значение ключа.
  • forEach, потому что на самом деле это немного сложнее, чем мы думали, на самом делеarray.forEach(function(currentValue, index, arr), thisValue)Это не синтаксический сахар для обычных циклов for, и есть много параметров и контекстов, которые необходимо учитывать во время выполнения, что может привести к задержке производительности;
  • map()Самый медленный, потому что его возвращаемое значение представляет собой совершенно новый массив равной длины, а накладные расходы на создание и назначение массива высоки.
  • for...inВсе свойства объекта, которые должны быть исчерпаны, включая пользовательские добавленные свойства, также могут быть пройдены. а такжеfor...inКлючStringТипа, идет процесс конвертации, накладные расходы относительно большие.

Суммировать

В реальной разработке нам нужно сочетать семантику, удобочитаемость и производительность программы, чтобы выбрать, какую схему использовать.

Если вам нужно сопоставить массив с другим массивом в соответствии с некоторыми правилами, вы должны использовать карту.

Если вам нужно выполнить простой обход, используйте forEach или for of.

Если вам нужно перебрать итератор, используйте for of.

Если вам нужно отфильтровать совпадающие элементы, используйте filterr.

Если вам нужно сначала сопоставить новый массив по правилам, а затем отфильтровать по условиям, используйте карту и фильтр.

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