ES6 的解构赋值前每次都创建一个对象吗?会加重 GC 的负担吗?
为了程序的易读性,我们会使用 ES6 的解构赋值:
这个例子的函数调用中,会真的产生一个对象吗?如果会,那大量的函数调用会白白生成很多有待 GC 释放的临时对象,那么就意味着在函数参数少时,还是需要尽量避免采用解构传参,而使用传统的:
首先从上面给的代码例子中,确实会产生一个对象。但是在实际项目中,有很大的概率是不需要产生这个临时对象的。
那么我们就分析一下你的示例代码。
Star r0 将当前在累加器中的值存储在寄存器 r0 中。
当我们使用解构赋值后:
我们可以看到,代码明显增加了很多,CreateObjectLiteral 创建了一个对象。其中不乏有 JumpIfUndefined、CallRuntime 、Throw 这种指令。
由于这个内存占用很小,因此我们加一个循环。
得到结果(为了便于阅读,我调整了输出格式):
可以看到多了因此内存分配,而且堆空间的使用也比之前多了。使用 --trace_gc_verbose 参数可以查看 gc 更详细的信息,还可以看到这些内存都是新生代,清理起来的开销还是比较小的。
通过逃逸分析,V8 引擎可以把临时对象去除。
如果我们还有一个函数,double,用于给一个数字加倍。
在 V8 引擎内部,会按照如下步骤进行逃逸分析处理:
把对函数 add 的调用进行内联展开,变成:
通过 V8 的逃逸分析,把本来分配到堆上的对象去除了。
不要做这种语法层面的微优化,引擎会去优化的,业务代码还是更加关注可读性和可维护性。如果你写的是库代码,可以尝试这种优化,把参数展开后直接传递,到底能带来多少性能收益还得看最终的基准测试。