夜下客

繁体版 简体版
夜下客 > JS修炼法则 > 第27章 从V8中启发的JS写法

第27章 从V8中启发的JS写法

0: 'a', 1: 'b', 2: 'c' };

Array.prototype.forEach.call(arrayLike, (v, i) => {

console.log(i, v);

});

原理:在类数组对象上调用数组内置的 Array.prototype 的方法。

这比在真正的数组中调用 forEach 慢,引擎数组的 forEach 在 V8 中是高度优化的。

真要这么做,应该先把 arrayLike 转化成一个真正的数组,然后对转化后的结果调用数组操作。

三.对象

3-1.初始化

JS 不像强类型语言在编译前对象结构就固定了,每个属性之间具有固定偏移量,因而可根据属性类型确定偏移量。

因为字典在内存中查找对象属性的位置效率很低,所以 V8 搞了隐藏类去记录偏移量,来模拟 java 编译的固定对象结构,优化属性的访问速度。

所以,对于对象的使用,应该避免隐藏类的改变。

建议:

使用字面量初始化多个对象时,保证命名属性的顺序一致。

使用字面量一次性初始化完整对象属性。

避免使用 delete 。同时若操作不当时,会退化成慢属性。

属性分为命名属性和元素属性。

// bad case

var obj1 = { age: 18 };

obj1.name = 'CS阿吉';

delete obj1.age;

// good case

var obj2 = { name: 'CS阿吉', age: 18 };

动态增删会导致JS在 V8 对应的隐藏类被改变,性能损耗在隐藏类的新建。

添加元素属性不会创建新的隐藏类

每个 JavaScript 对象都有一个关联的隐藏类,用于保存有关对象形状的信息,是 V8 优化编译器和内联缓存的非常重要的组成部分。

经过优化编译器的代码只能真对固定结构,一旦代码执行中,对象结构动态变化,则机器码失效

隐藏类描述了对象的命名属性布局,其作用是优化命名属性的访问速度。简单来说,隐藏类不包含元素属性信息。

3-2.属性数量

尽量控制一个对象的命名属性个数 <= 10个。

因为命名属性的个数不同,会采用不同的存储方式。

访问速度由快到慢依此是:对象内属性,快属性,慢属性。

因为对象内属性会直接存储到对象本身,快属性使用的是连续存储,慢属性采用的是字典存储。

3-3.扩展运算符

const a = {name: 'hh'}

const rest1 = {

...a,

age: 10,

const rest2 = {

age: 10,

...a,

rest1 更高效,V8团队直接给出的书写建议。

四.函数

4-1.解析方式

V8 采用的是混合编译执行+解释执行,即JIT。

V8 有两种解析方式:全解析(eager parse)、预解析(lazy parse)。

全解析是针对变量。预解析针对非IIFE的函数定义,只解析函数声明,不会解析函数内部代码,不会为函数内部代码生成 AST。

// 预解析:只解析了函数声明,函数体整体作为字符串存储到堆中。等 add1 执行的时候,才取出字符串解析AST,再编译为字节码执行。

function add1(a, b) {

return a + b;

// 全解析:编译阶段保存解析+编译得到字节码

const add2 = (function (a, b) {

return a + b;

})();

// add2 执行速度会更快。

『加入书签,方便阅读』