V8中的元素种类

可以将任意字符串用作JavaScript对象的属性名称。但是对于某些特殊的名称子集,在JavaScript引擎中进行特殊的优化是有意义的。数字数组索引就是这种情况之一



尽管在大多数情况下,这些属性的行为与其他属性没有区别,但出于优化目的,V8引擎将它们与其余属性分开存储,并以特殊方式对其进行处理。内部V8这种特性被称为元素元素的对象)。非常合乎逻辑:对象具有可通过名称访问的属性,而数组具有可通过索引访问的元素。



基本元素等级



在执行JavaScript代码期间,V8会跟踪每个数组的元素种类-它存储哪些元素。该信息使引擎可以更好地优化其阵列操作。例如,内置类似功能mapreduce或者forEach专用于每个种类的元素。



例如,考虑如下数组:



const array = [1, 2, 3];


它包含哪些元素?从操作员的角度来看,typeof一切都很简单-它们是type元素number这是所有能在JavaScript中这样说,大约他们:语言没有区别intfloatdouble但是,发动机级别有所不同。此数组的元素类型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- ( ).




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]>



All Articles