Une chaîne arbitraire peut être utilisée comme nom de propriété pour un objet JavaScript. Mais pour certains sous-ensembles spéciaux de noms, il est logique de faire des optimisations spéciales dans les moteurs JavaScript. Un tel cas est celui des indices de tableau numérique .
Bien que dans la plupart des cas, ces propriétés ne se distinguent pas des autres, le moteur V8, à des fins d'optimisation, les stocke séparément des autres et les traite de manière spéciale. À l'intérieur de V8, ces propriétés sont appelées éléments ( éléments ) de l'objet. Assez logique: les objets ont des propriétés accessibles par nom, et les tableaux ont des éléments accessibles par index.
Grades élémentaires de base
Pendant l'exécution du code JavaScript, V8 garde une trace du type d'éléments de chaque tableau - quels éléments il stocke. Ces informations permettent au moteur de mieux optimiser ses opérations de baie. Par exemple, des fonctions intégrées comme map
, reduce
ou sont forEach
spécialisés pour chaque type d'élément.
Considérez, par exemple, un tableau comme celui-ci:
const array = [1, 2, 3];
Quels éléments contient-il? Du point de vue de l'opérateur, typeof
tout est simple - ce sont des éléments du type number
. Et c'est tout ce que l'on peut dire Ă leur sujet depuis JavaScript: le langage ne distingue pas int
, float
et double
. Cependant, il existe des différences au niveau du moteur. Le type d'éléments de ce tableau est PACKED_SMI _ELEMENTS
. En termes V8, SMI est un format spécial pour stocker de petits entiers. Ce qui signifie que PACKED
nous y reviendrons un peu plus tard.
L'ajout d'un nombre fractionnaire à un tableau rend ses éléments plus généraux:
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
L'ajout d'une ligne rend le type d'éléments encore plus général:
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
array.push('x');
// : PACKED_ELEMENTS
Il existe trois principaux types d'éléments dans ce code:
SMI_ELEMENTS
- pour les petits entiersDOUBLE_ELEMENTS
- pour les nombres Ă virgule flottante et les entiers trop grands pourSMI
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]>