Uma string arbitrária pode ser usada como um nome de propriedade para um objeto JavaScript. Mas, para alguns subconjuntos especiais de nomes, faz sentido fazer otimizações especiais em mecanismos JavaScript. Um desses casos são os índices de matriz numérica .
Embora na maioria dos casos essas propriedades se comportem indistinguivelmente de qualquer outra, o motor V8, para fins de otimização, as armazena separadamente das demais e as processa de maneira especial. No V8, essas propriedades são chamadas de elementos ( elementos ) do objeto. Muito lógico: os objetos têm propriedades acessíveis por nome e os arrays têm elementos acessíveis por índice.
Graus de elemento básico
Durante a execução do código JavaScript, o V8 controla os tipos de elementos de cada array - quais elementos ele armazena. Essas informações permitem que o mecanismo otimize melhor as operações do array. Por exemplo, funções integradas como map
, reduce
ou são forEach
especializadas para cada tipo de elemento.
Considere, por exemplo, uma matriz como esta:
const array = [1, 2, 3];
Que elementos contém? Do ponto de vista do operador, typeof
tudo é simples - são elementos do tipo number
. E isso é tudo que pode ser dito sobre eles a partir de dentro JavaScript: a linguagem não distingue int
, float
e double
. No entanto, existem diferenças no nível do motor. O tipo de elementos dessa matriz é PACKED_SMI _ELEMENTS
. Em termos de V8, SMI é um formato especial para armazenar pequenos inteiros. O que significa PACKED
que veremos um pouco mais tarde.
Adicionar um número fracionário a uma matriz torna seus elementos mais gerais:
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
Adicionar uma linha torna o tipo de elementos ainda mais geral:
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
array.push('x');
// : PACKED_ELEMENTS
Existem três tipos principais de elementos neste código:
SMI_ELEMENTS
- para pequenos inteirosDOUBLE_ELEMENTS
- para números de ponto flutuante e inteiros muito 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
- ( ).
V8 . :
. , DOUBLE
. , DOUBLE
. , , - HOLEY
, PACKED
.
, V8 29 ( 22 — . .). .
, , . , . .
, . , 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, .
. 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]>