Ответ 1
В первой версии JavaScript не было массивов. Позднее они были представлены в качестве подкласса этой "матери всех объектов": Object
. Вы можете легко протестировать это, выполнив следующее:
var foo = [1,2,3,4];
for (var n in foo)
{//check if n is equal (value and type) to itself, coerced to a number
console.log(n === +(n) ? 'Number' : 'String');
}
Это будет записывать String
снова и снова. Внутренне все цифровые клавиши преобразуются в строки. Свойство Length просто извлекает самый высокий индекс и добавляет к нему 1. Больше ничего. Когда вы показываете свой массив, объект повторяется, и для каждого ключа применяются те же правила, что и для любого объекта: сначала проверяется экземпляр, а затем прототип (ы)... поэтому, если мы немного изменим наш код:
var foo = [1,2,3,4];
foo[9] = 5;
for (var n in foo)
{
if (foo.hasOwnProperty(n))
{//check if current key is an array property
console.log(n === +(n) ? 'Number' : 'String');
}
}
Вы заметите, что массив имеет только 5 собственных свойств, ключи undefined
4-8 - undefined, потому что не было соответствующего значения, найденного в экземпляре, и ни в одном из базовых прототипов. Короче: массивы - это не массивы, а объекты, которые ведут себя одинаково.
Как заметил Тим, вы можете иметь экземпляр массива с свойством undefined, которое существует внутри этого объекта:
var foo = [1,2,undefined,3];
console.log(foo[2] === undefined);//true
console.log(foo[99] === undefined);//true
Но опять-таки есть разница:
console.log((foo.hasOwnProperty('2') && foo[2] === undefined));//true
console.log((foo.hasOwnProperty('99') && foo[99] === undefined));//false
RECAP, ваши три основных вопроса:
-
Массивы - это объекты, которые позволяют ссылаться на их свойства с числовыми экземплярами
-
Значения
undefined
не существуют, они просто являются возвращаемым значением по умолчанию, когда JS сканирует объект и прототипы и не может найти то, что вы ищете: "Извините, что вы меня спрашиваете undefined в моей книге." это то, что он говорит. - Работа с массивными массивами undefined не влияет на размер самого объекта, но доступ к ключу undefined может быть очень, очень незначительно медленнее, потому что необходимо также проверять прототипы.
Update:
Просто цитируя Ecma std:
15.4 Объекты массива
Объекты массива предоставляют особый подход к определенному классу имен свойств. Имя свойства P (в форме String value) является индексом массива тогда и только тогда, когда ToString (ToUint32 (P)) равен P и ToUint32 (P) не равно 2 ^ 32 1. Свойство, имя свойства которого является индексом массива, также называется элементом. Каждый объект Array имеет которое всегда является неотрицательным целым числом меньше 2 ^ 32. Значение длины свойство численно больше имени каждого свойства, имя которого является индексом массива; когда свойство объекта Array создается или изменяется, другие свойства корректируются по мере необходимости для поддержания этого инвариант. В частности, всякий раз, когда добавляется свойство, чье имя является индексом массива, свойство length при необходимости изменилось, чтобы было больше, чем числовое значение этого индекса массива; и всякий раз, когда длина свойство изменяется, каждое свойство, имя которого является индексом массива, значение которого не меньше, чем новое длина автоматически удаляется. Это ограничение применяется только к собственным свойствам объекта Array и не зависящие от свойств индекса длины или массива, которые могут быть унаследованы от его прототипов.Объект, O, называется разреженным, если следующий алгоритм возвращает true:
1. Пусть len является результатом вызова внутреннего метода [[Get]] O с аргументом "length".
2. Для каждого целого я в диапазоне 0≤i
а. Пусть elem является результатом вызова внутреннего метода O [[GetOwnProperty]] с аргументом ToString (я).
б. Если elem undefined, верните true.
3. Верните значение false.