Se puede utilizar una cadena arbitraria como nombre de propiedad para un objeto JavaScript. Pero para algunos subconjuntos especiales de nombres, tiene sentido hacer optimizaciones especiales en motores JavaScript. Uno de esos casos son los índices de matriz numérica .
Aunque en la mayoría de los casos estas propiedades se comportan indistinguibles de cualquier otra, el motor V8, a efectos de optimización, las almacena por separado del resto y las procesa de forma especial. Dentro de V8, estas propiedades se denominan elementos ( elementos ) del objeto. Bastante lógico: los objetos tienen propiedades a las que se puede acceder por nombre y las matrices tienen elementos a los que se puede acceder por índice.
Calificaciones de elementos básicos
Durante la ejecución del código JavaScript, V8 realiza un seguimiento del tipo de elementos de cada matriz, qué elementos almacena. Esta información permite que el motor mejor a optimizar sus operaciones de matriz. Por ejemplo, una función de funciones como map
, reduce
o están forEach
especializados para cada tipo de elemento.
Considere, por ejemplo, una matriz como esta:
const array = [1, 2, 3];
¿Qué elementos contiene? Desde el punto de vista del operador, typeof
todo es simple: son elementos del tipo number
. Y esto es todo lo que se puede decir sobre ellos desde dentro de JavaScript: el lenguaje no distingue int
, float
y double
. Sin embargo, existen diferencias a nivel de motor. El tipo de elementos de esta matriz es PACKED_SMI _ELEMENTS
. En términos de V8, SMI es un formato especial para almacenar números enteros pequeños. Lo que significa PACKED
que veremos un poco más adelante.
Adición de un número fraccionario a una matriz hace que sus elementos más general:
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
Agregar una línea allí hace que el tipo de elementos sea aún más general:
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
array.push('x');
// : PACKED_ELEMENTS
Hay tres principales tipos de elementos en este código:
SMI_ELEMENTS
- para números enteros pequeñosDOUBLE_ELEMENTS
- para números de coma flotante y enteros demasiado grandes paraSMI
ELEMENTS
— ,SMI
DOUBLE
, . , PACKED_ELEMENTS
PACKED_DOUBLE_ELEMENTS
.
:
- V8 .
- — , .
- .
PACKED
HOLEY
(dense), (packed), . "" ( , ) (sparse), "" (holey):
const array = [1, 2, 3, 4.56, 'x'];
// : PACKED_ELEMENTS
array.length; // 5
array[9] = 1; // array[5] array[9]
// : HOLEY_ELEMENTS
V8 , . , , .
(SMI_ELEMENTS
, DOUBLE_ELEMENTS
ELEMENTS
) (PACKED
), (HOLEY
), . , PACKED_SMI_ELEMENTS
PACKED_DOUBLE_ELEMENTS
, HOLEY_SMI_ELEMENTS
.
:
- (
PACKED
), (HOLEY
). - , .
-
PACKED
-HOLEY
- ( ).
. , DOUBLE
. , DOUBLE
. , , - HOLEY
, PACKED
.
, , . , . .
, . , V8 , .
, , . , , . , , array[42]
, array.length === 5
. 42
, , . , V8 , , , .
, :
for (let i = 0, item; (item = items[i]) != null; i++) {
doSomething(item);
}
items[items.length]
, .
:
for (let index = 0; index < items.length; index++) {
const item = items[index];
doSomething(item);
}
, items
— iterable- ( , ), for-of
:
for (const item of items) {
doSomething(item);
}
forEach
:
items.forEach((item) => {
doSomething(item);
});
, for-of
forEach
for
.
, ! :
function maximum(array) {
let max = 0;
for (let i = 0; i <= array.length; i++) { //
if (array[i] > max) max = array[i];
}
return max;
}
array[array.length]
, , : , , V8 undefined
. , .
, , , V8 .
, . , -0
PACKED_DOUBLE_ELEMENTS
.
const array = [3, 2, 1, +0];
// PACKED_SMI_ELEMENTS
array.push(-0);
// PACKED_DOUBLE_ELEMENTS
, , .
-0
, -0
+0
(, , ).
NaN
Infinity
. , , SMI_ELEMENTS
DOUBLE_ELEMENTS
.
const array = [3, 2, 1];
// PACKED_SMI_ELEMENTS
array.push(NaN, Infinity);
// PACKED_DOUBLE_ELEMENTS
, . , PACKED_SMI_ELEMENTS
, .
array-like objects
JavaScript, — , DOM API — , . " " (array-like) :
const arrayLike = {};
arrayLike[0] = 'a';
arrayLike[1] = 'b';
arrayLike[2] = 'c';
arrayLike.length = 3;
length
. . :
Array.prototype.forEach.call(arrayLike, (value, index) => {
console.log(`${ index }: ${ value }`);
});
// : '0: a', '1: b', '2: c'.
forEach
, . , , array-like - , :
const actualArray = Array.prototype.slice.call(arrayLike, 0);
actualArray.forEach((value, index) => {
console.log(`${ index }: ${ value }`);
});
// : '0: a', '1: b', '2: c'.
.
, arguments
— . , , :
const logArgs = function() {
Array.prototype.forEach.call(arguments, (value, index) => {
console.log(`${ index }: ${ value }`);
});
};
logArgs('a', 'b', 'c');
// : '0: a', '1: b', '2: c'.
arguments
rest parameters, , ECMAScript 2015. , .
function logArgs(...args) {
args.forEach((value, index) => {
console.log(`${ index }: ${ value }`);
});
};
logArgs('a', 'b', 'c');
// : '0: a', '1: b', '2: c'.
arguments
.
, array-like , .
, , , . :
const each = (array, callback) => {
for (let index = 0; index < array.length; ++index) {
const item = array[index];
callback(item);
}
};
const doSomething = (item) => console.log(item);
each([], () => {});
each(['a', 'b', 'c'], doSomething);
// `each` `PACKED_ELEMENTS`.
// V8 inline- (inline cache, IC), `each`
// . V8
// , , `array.length`
// `array[index]` - ,
// , .
// `each` .
// `PACKED_ELEMENTS`, V8 . ,
// .
each([1.1, 2.2, 3.3], doSomething);
// `each` `PACKED_DOUBLE_ELEMENTS`.
// - , V8 `each` ,
// `array.length` `array[index]` .
//
// , .
each([1, 2, 3], doSomething);
// `each` `PACKED_SMI_ELEMENTS`.
// `each`,
// .
Array.prototype.forEach
, , , .
. V8, . , :
const array = new Array(3);
// ,
// `HOLEY_SMI_ELEMENTS`, ,
//
array[0] = 'a';
// , , !
// `HOLEY_ELEMENTS`.
array[1] = 'b';
array[2] = 'c';
// ,
// `HOLEY_ELEMENTS`
// `PACKED_ELEMENTS`.
, , — !
:
const array = ['a', 'b', 'c'];
// : PACKED_ELEMENTS
, , push
.
const array = [];
// ...
array.push(someValue);
// ...
array.push(someOtherValue);
, , d8
( jsvu). :
out/x64.debug/d8 --allow-natives-syntax
REPL d8, . %DebutPrint(object)
( elements
):
d8> const array = [1, 2, 3]; %DebugPrint(array);
DebugPrint: 0x1fbbad30fd71: [JSArray]
- map = 0x10a6f8a038b1 [FastProperties]
- prototype = 0x1212bb687ec1
- elements = 0x1fbbad30fd19 <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
- length = 3
- properties = 0x219eb0702241 <FixedArray[0]> {
#length: 0x219eb0764ac9 <AccessorInfo> (const accessor descriptor)
}
- elements= 0x1fbbad30fd19 <FixedArray[3]> {
0: 1
1: 2
2: 3
}
[...]
--trace-elements-transitions
. , V8 .
$ cat my-script.js
const array = [1, 2, 3];
array[3] = 4.56;
$ out/x64.debug/d8 --trace-elements-transitions my-script.js
elements transition [PACKED_SMI_ELEMENTS -> PACKED_DOUBLE_ELEMENTS] in ~+34 at x.js:2 for 0x1df87228c911 <JSArray[3]> from 0x1df87228c889 <FixedArray[3]> to 0x1df87228c941 <FixedDoubleArray[22]>