ECMAScript 6: дыры в массивах

Добавлено 14 ноября 2015 в 16:40

1 Дыры в объектах Array

Дыры – это индексы внутри объекта Array, у которых нет связанного с ними элемента. Другими словами: у массива arr дыра в индексе i если:

  • 0 ≤ i < arr.length
  • !(i in arr)

Например, следующий массив имеет дыру в индексе 1:

    > let arr = ['a',,'b']
    'use strict'
    > 0 in arr
    true
    > 1 in arr
    false
    > 2 in arr
    true
    > arr[1]
    undefined

Для более подробной информации посмотрите раздел “Holes in Arrays” в “Speaking JavaScript”.

2 ECMAScript 6: дыры обрабатываются как элементы undefined

Общее правило для методов Array, появившихся в ES6: каждая дыра обрабатывается, как если бы она была элементом типа undefined. Например:

    > Array.from(['a',,'b'])
    [ 'a', undefined, 'b' ]
    > [,'a'].findIndex(x => true)
    0
    > [...[,'a'].entries()]
    [ [ 0, undefined ], [ 1, 'a' ] ]

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

3 Методы объекта Array и дыры

3.1 Array.from()

Array.from() преобразует дыры в undefined:

    > Array.from(['a',,'b'])
    [ 'a', undefined, 'b' ]

Со вторым аргументом, он работает как map(), но не игнорирует дыры:

    > Array.from(new Array(3), (x,i) => i)
    [ 0, 1, 2 ]

3.2 Оператор распространения (...)

Внутри объектов Array оператор распространения (...) работает так же, как Array.from() (но его операнд должен быть итерируемым, в то время как Array.from() может обрабатывать всё, что подобно массиву).

    > [...['a',,'b']]
    [ 'a', undefined, 'b' ]

3.3 Методы Array.prototype

В ECMAScript 5 поведение варьируется уже незначительно. Например:

  • forEach(), filter(), every() и some() игнорируют дыры;
  • map() пропускает, но сохраняет дыры;
  • join() и toString() обрабатывают дыры, как если бы они были элементами undefined, но интерпретируют null и undefined, как пустые строки.

ECMAScript 6 добавляет новые типы поведения:

  • copyWithin() создает дыры при их копировании (т.е. он удаляет элементы, если это необходимо);
  • entries(), keys(), values() обрабатывают каждую дыру, как если бы она была элементом undefined;
  • find() и findIndex() делают то же самое;
  • fill() не заботится, есть ли элементы в индексах или нет.

Следующая таблица описывает, как методы Array.prototype обрабатывают дыры.

МетодДыры 
concatсохраняются['a',,'b'].concat(['c',,'d']) → ['a',,'b','c',,'d']
copyWithinсохраняются[,'a','b',,].copyWithin(2,0) → [,'a',,'a']
entriesэлементы[...[,'a'].entries()] → [[0,undefined], [1,'a']]
everyигнорируются[,'a'].every(x => x==='a') → true
fillзаполняютсяnew Array(3).fill('a') → ['a','a','a']
filterудаляются['a',,'b'].filter(x => true) → ['a','b']
findэлементы[,'a'].find(x => true) → undefined
findIndexэлементы[,'a'].findIndex(x => true) → 0
forEachигнорируются[,'a'].forEach((x,i) => log(i)); → 1
indexOfигнорируются[,'a'].indexOf(undefined) → -1
joinэлементы[,'a',undefined,null].join('#') → '#a##'
keysэлементы[...[,'a'].keys()] → [0,1]
lastIndexOfигнорируются[,'a'].lastIndexOf(undefined) → -1
mapсохраняются[,'a'].map(x => 1) → [,1]
popэлементы['a',,].pop() → undefined
pushсохраняютсяnew Array(1).push('a') → 2
reduceигнорируются['#',,undefined].reduce((x,y)=>x+y) → '#undefined'
reduceRightигнорируются['#',,undefined].reduceRight((x,y)=>x+y) → 'undefined#'
reverseсохраняются['a',,'b'].reverse() → ['b',,'a']
shiftэлементы[,'a'].shift() → undefined
sliceсохраняются[,'a'].slice(0,1) → [,]
someигнорируются[,'a'].some(x => x !== 'a') → false
sortсохраняются[,undefined,'a'].sort() → ['a',undefined,,]
spliceсохраняются['a',,].splice(1,1) → [,]
toStringэлементы[,'a',undefined,null].toString() → ',a,,'
unshiftсохраняются[,'a'].unshift('b') → 3
valuesэлементы[...[,'a'].values()] → [undefined,'a']

Примечания:

  • методы ES6 помечены галочкой (✓)
  • JavaScript игнорирует замыкающие запятые: ['a',,].length → 2
  • в таблице использовалась вспомогательная функция: const log = console.log.bind(console);

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

Что касается дыр в объектах Array, сейчас существует только одно правило – никаких правил. Поэтому вы должны по возможности избегать дыр (на производительность они также влияют отрицательно). Если вы не можете избежать их использования, то таблица в предыдущем разделе поможет вам.

5 Дополнительные источники

  • ECMAScript 5: глава “Arrays” в “Speaking JavaScript”
  • ECMAScript 6: глава “New Array features” в “Exploring ES6”

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.


Сообщить об ошибке