JavaScriptオブジェクトのプロパティ名として任意の文字列を使用できます。ただし、名前の一部の特別なサブセットについては、JavaScriptエンジンで特別な最適化を行うことは理にかなっています。そのようなケースの1つは、数値配列インデックスです。
ほとんどの場合、これらのプロパティは他のプロパティと区別がつかないように動作しますが、V8エンジンは、最適化の目的で、他のプロパティとは別に保存し、特別な方法で処理します。V8内では、このようなプロパティはオブジェクトの要素(要素)と呼ばれます。かなり論理的です。オブジェクトには名前でアクセスできるプロパティがあり、配列にはインデックスでアクセスできる要素があります。
基本的な要素のグレード
JavaScriptコードの実行中、V8は各配列の要素の種類(格納する要素)を追跡します。この情報により、エンジンはアレイ操作をより適切に最適化できます。たとえば、、またはなどの組み込み関数はmap、要素の種類ごとに特化されています。reduceforEach
たとえば、次のような配列について考えてみます。
const array = [1, 2, 3];
どのような要素が含まれていますか?オペレーターの観点からは、typeofすべてが単純です-それらはタイプの要素ですnumber。言語は区別しません。そして、これには、JavaScript内からそれらについて言うことができることすべてであるint、floatとdouble。ただし、エンジンレベルでは違いがあります。この配列の要素の種類はPACKED_SMI _ELEMENTSです。V8の用語では、SMIは小さな整数を格納するための特別な形式です。つまりPACKED、少し後で見ていきます。
配列に小数を追加すると、その要素がより一般的になります。
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
それに行を追加すると、要素の種類がさらに一般的になります。
const array = [1, 2, 3];
// : PACKED_SMI_ELEMENTS
array.push(4.56);
// : PACKED_DOUBLE_ELEMENTS
array.push('x');
// : PACKED_ELEMENTS
このコードには、主に3種類の要素があります。
SMI_ELEMENTS-小さい整数の場合DOUBLE_ELEMENTS-浮動小数点数と整数が大きすぎてSMIELEMENTS— ,SMIDOUBLE
, . , 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]>