ES6 的解构赋值前每次都创建一个对象吗?会加重 GC 的负担吗?

为了程序的易读性,我们会使用 ES6 的解构赋值:

这个例子的函数调用中,会真的产生一个对象吗?如果会,那大量的函数调用会白白生成很多有待 GC 释放的临时对象,那么就意味着在函数参数少时,还是需要尽量避免采用解构传参,而使用传统的:

首先从上面给的代码例子中,确实会产生一个对象。但是在实际项目中,有很大的概率是不需要产生这个临时对象的。

那么我们就分析一下你的示例代码。

Star r0 将当前在累加器中的值存储在寄存器 r0 中。

当我们使用解构赋值后:

我们可以看到,代码明显增加了很多,CreateObjectLiteral 创建了一个对象。其中不乏有 JumpIfUndefined、CallRuntime 、Throw 这种指令。

由于这个内存占用很小,因此我们加一个循环。

得到结果(为了便于阅读,我调整了输出格式):

可以看到多了因此内存分配,而且堆空间的使用也比之前多了。使用 --trace_gc_verbose 参数可以查看 gc 更详细的信息,还可以看到这些内存都是新生代,清理起来的开销还是比较小的。

通过逃逸分析,V8 引擎可以把临时对象去除。

如果我们还有一个函数,double,用于给一个数字加倍。

在 V8 引擎内部,会按照如下步骤进行逃逸分析处理:

把对函数 add 的调用进行内联展开,变成:

通过 V8 的逃逸分析,把本来分配到堆上的对象去除了。

不要做这种语法层面的微优化,引擎会去优化的,业务代码还是更加关注可读性和可维护性。如果你写的是库代码,可以尝试这种优化,把参数展开后直接传递,到底能带来多少性能收益还得看最终的基准测试。