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”

Теги

ECMAScriptES6 / ECMAScript 6front-endJavaScriptВеб-разработка / WebDev / Web DevelopmentМассивФронтенд