Статьи

Применить и массивы: три хитрости


В этом блоге описаны три хитрости для работы с массивами с помощью apply.

Метод применения

применить это метод, который есть у всех функций. Его подпись

    func.apply(thisValue, [arg1, arg2, ...])

Игнорируя thisValue, приведенный выше вызов эквивалентен:

    func(arg1, arg2, ...)

Таким образом, apply позволяет нам разворачивать массив «в» аргументы вызова функции. Давайте рассмотрим три уловки, которые позволяют нам выполнить.

Трюк 1: передать массив функции, которая не принимает массивы

В JavaScript нет функции, которая возвращает максимум массива чисел. Однако есть функция Math.max, которая работает с произвольным числом числовых аргументов. Благодаря применению, мы можем использовать эту функцию для наших целей:

    > Math.max.apply(null, [10, -1, 5])
    10

Трюк 2: устранить дыры в массивах

Отверстия в массивах

Краткое напоминание: в JavaScript массив представляет собой карту из чисел в значения. Таким образом, существует разница между отсутствующим элементом (отверстием) и элементом со значением undefined
[1] . Первый пропускается итерационными методами Array.prototype (forEach, map и т. Д.), Последний — нет:

    > ["a",,"b"].forEach(function (x) { console.log(x) })
    a
    b
    
    > ["a",undefined,"b"].forEach(function (x) { console.log(x) })
    a
    undefined
    b

Вы также можете проверить наличие отверстий с помощью оператора in.

    > 1 in ["a",,"b"]
    false
    > 1 in ["a", undefined, "b"]
    true

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

    > ["a",,"b"][1]
    undefined
    > ["a", undefined, "b"][1]
    undefined

Устранение дыр

С помощью apply и Array (которые можно использовать как функцию или конструктор) вы можете превратить отверстия в неопределенные элементы:

    > Array.apply(null, ["a",,"b"])
    [ 'a', undefined, 'b' ]

Вышеприведенное работает, потому что apply не игнорирует дыры, а передает их как неопределенные аргументы функции:

    > function returnArgs() { return [].slice.call(arguments) }
    > returnArgs.apply(null, ["a",,"b"])
    [ 'a', undefined, 'b' ]

Увы, вы столкнулись с причудой здесь. Если Array получает один числовой аргумент, создается пустой массив, длина которого равна числу
[2] :

    > Array.apply(null, [ 3 ])
    [ , ,  ]

Поэтому, если вам нужна эта функциональность, лучше написать свою собственную функцию:

    function fillHoles(arr) {
        var result = [];
        for(var i=0; i < arr.length; i++) {
            result[i] = arr[i];
        }
        return result;
    }

Пример:

    > fillHoles(["a",,"b"])
    [ 'a', undefined, 'b' ]

Подчеркивание дает вам
функцию
_.compact, которая удаляет все ложные значения, включая дыры:

    > _.compact(["a",,"b"])
    [ 'a', 'b' ]
    > _.compact(["a", undefined, "b"])
    [ 'a', 'b' ]
    > _.compact(["a", false, "b"])
    [ 'a', 'b' ]

Трюк 3: сгладить массив

Задача: превратить массив массивов с элементами в просто массив элементов. Опять же, мы используем возможность развернуть apply, чтобы concat сделал эту работу за нас:

    > Array.prototype.concat.apply([], [["a"], ["b"]])
    [ 'a', 'b' ]

Элементы не-массива добавляются как:

    > Array.prototype.concat.apply([], [["a"], "b"])
    [ 'a', 'b' ]

applyS thisValue должно быть здесь [], потому что concat — это метод, а не не-метод. Выполняется только один уровень выравнивания:

    > Array.prototype.concat.apply([], [[["a"]], ["b"]])
    [ [ 'a' ], 'b' ]

Если вы хотите, чтобы ваш код был информативным, вам следует рассмотреть альтернативные варианты, в том числе реализовать собственную функцию с правильным именем. У Underscore есть _.flatten, который обрабатывает любой уровень вложенности:

    > _.flatten([[["a"]], ["b"]])
    [ 'a', 'b' ]

Рекомендации

  1. JavaScript: разреженные массивы против плотных массивов
  2. ECMAScript.next: Array.from () и Array.of ()