Почему функции map, every и другие функции массива пропускают пустые значения?

Фон

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

Пример

const arr1 = [, , 3, 4]
const arr2 = [1, 2, 3, 4]
const result = arr1.every((item, index) => item === arr2[index])
console.log(result) // true (HOW????)

Ответ 1

Это расширение того факта, что forEach посещает только те элементы, которые действительно существуют. Я не знаю, что есть более глубокое "почему" для этого, кроме того, что не имеет смысла вызывать обратный вызов для отсутствующего элемента.

Вы можете реализовать эти элементы (если это мир), используя:

  1. Распространить нотацию или
  2. Array.from или
  3. Array.prototype.values или
  4. Array.prototype.entries

... или, возможно, некоторые другие.

const a = [, , 3];
console.log(a.hasOwnProperty(0)); // false
const b = [...a];
console.log(b.hasOwnProperty(0)); // true
const c = Array.from(a);
console.log(b.hasOwnProperty(0)); // true

Ответ 2

Потому что языковой дизайн так говорит. 🤷🏻♂️

Смотрите спецификацию, которая гласит:

  • Повторите, пока k & lt; Len
    • Пусть Pk будет ToString (k).
    • Пусть kPresent будет HasProperty(O, Pk).
    • ReturnIfAbrupt (kPresent).
    • Если kPresent истинно, то

... затем сделайте операцию.

Поскольку значение никогда не было присвоено свойствам 0 и 1, тест HasProperty дает false, поэтому они пропускаются по правилу If.

Ответ 3

По документам .every():

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

Итак, вы вызываете .every() только с истинными значениями array1:

const arr1 = [, , 3, 4]

arr1.every((x, idx) => {
 console.log('element: ${x}', 'index: ${idx}');
 return true;
})

Ответ 4

Встроенные итерационные функции (как описано другими и определены в спецификациях) будут пропускать значения, когда HasProperty имеет значение false.

You could create your own shim for all which would check each value. This would be an expansion of the prototype. Alternatively, turning it into a function if this code were to be used in a wider scope would be a better design и require a slightly different call.

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

const arr1 = [, , 3, 4];
const arr2 = [1, 2, 3, 4];

Array.prototype.all = function(callback){
 for(let i = 0; i < this.length; i++){
     if(callback(this[i],i,this)) return false;
 }
 return true;
};

const result2 = arr1.all((item, index) => item === arr2[index]);
console.log(result2); // false