可以将任意字符串用作JavaScript对象的属性名称。但是对于某些特殊的名称子集,在JavaScript引擎中进行特殊的优化是有意义的。数字数组索引就是这种情况之一。
尽管在大多数情况下,这些属性的行为与其他属性没有区别,但出于优化目的,V8引擎将它们与其余属性分开存储,并以特殊方式对其进行处理。内部V8这种特性被称为元素(元素的对象)。非常合乎逻辑:对象具有可通过名称访问的属性,而数组具有可通过索引访问的元素。
基本元素等级
在执行JavaScript代码期间,V8会跟踪每个数组的元素种类-它存储哪些元素。该信息使引擎可以更好地优化其阵列操作。例如,内置类似功能map
,reduce
或者forEach
专用于每个种类的元素。
例如,考虑如下数组:
const array = [1, 2, 3];
它包含哪些元素?从操作员的角度来看,typeof
一切都很简单-它们是type元素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
此代码中主要有三种元素:
SMI_ELEMENTS
-小整数DOUBLE_ELEMENTS
-对于浮点数和整数,对于SMI
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]>