Vue源码中关于拦截数组方法的困惑?

// Vue 2.0 src/core/observer/array.js

;[
    'push',
    'pop',
    'shift',
    'unshift',
    'splice',
    'sort',
    'reverse'
]
    .forEach(function (method) {
        // cache original method
        const original = arrayProto[method]
        def(arrayMethods, method, function mutator() {
            // avoid leaking arguments:
            // http://jsperf.com/closure-with-arguments(失效)
            let i = arguments.length
            const args = new Array(i)
            while (i--) {
                args[i] = arguments[i]
            }
            const result = original.apply(this, args)

注释中说避免泄露参数。不明白如果不按源码中那么写,是如何有泄露参数的风险?

阅读 2.4k
3 个回答

这里的内存泄露是说,防止新建没必要的arguments对象,来优化性能,旧的JS引擎里arguments对象和函数参数之间有链接。如果你改了arguments对象里的值,对应的函数参数也会变,但是现在的JS引擎对arguments对象已经做过优化了,直接用arguments对象不会影响性能,vue是为了保证所有环境的性能,所以不直接用arguments对象。

我觉得楼上乔治老哥的说法已经很详细了 补充一下我的见解:
首先这里的leaking arguments
指的其实是:传递arguments对象给任何方法的这一行为
那么这里注释里写了avoid避免,那么为什么要避免这一行为呢?
因为在vue2.0那个时候 JS引擎V8会跳过对leaking arguments的优化,这也将使性能相当慢
同时arguments对象是没有filter、map和forEach这样内建到数组内的方法的,
于是这种写法既能保证V8的优化,又能得到一个具有原型对象所有内建方法的完整数组对象,
现在的V8引擎是能够针对leaking arguments进行优化的
所以我们看到现在vue2.x的最新版本2.7.14里面这块的写法:
image.png
已经变成直接拿arguments对象直接给apply方法了

参考资料:
避免修改和传递arguments给其他方法 — 影响优化-By@berkana/github

如果不按照这种方式处理参数,直接在内部函数中使用外部函数的参数,可能会导致以下问题:

闭包引用:如果内部函数 mutator 直接引用外部函数的参数,那么在闭包中会保留对这些参数的引用。这可能导致内存泄漏,因为这些参数的引用将被持续保留,即使它们在外部函数执行完后不再需要。

参数修改:如果内部函数 mutator 直接修改外部函数的参数,可能会导致意外的副作用。因为外部函数的参数是在外部调用时传递的,如果在内部函数中直接修改这些参数,可能会影响到外部函数的调用者。

通过将外部函数的参数复制到新的数组中,并在内部函数中使用这个新数组,可以避免闭包引用和参数修改的问题,确保代码的正确性和可维护性。
GPT回答的

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏