Array.map() 的箭头函数,.bind 或 argThis?垃圾回收+性能
Arrow functions, .bind or argThis for Array.map()? garbage collection + performance
我正在从事一个每天处理数十亿个请求的项目,我非常关心性能和内存使用情况。
昨天我对每分钟执行很多很多次的代码进行了一些更改,并且我使用箭头函数进行一些映射,但是团队要求我在使用 .map 时始终使用 argsThis,但我没有'不明白为什么,所以我做了很多基准测试,结果正好相反。
Benchmark(绑定 vs 箭头函数 vs ArgsThis)
他们的论点是,垃圾回收在使用箭头函数时是最糟糕的,并且这些案例没有显示真实场景,因为它的上下文非常浅。
基准测试显示箭头函数要快得多,有什么我需要考虑的吗?
谢谢!
编辑:
问题是我们需要来自上层上下文的变量的情况,例如:
function doSomething() {
const someUpperContextValue = 5;
[1, 2, 3].map((currentValue) => calculateValues(someOutterContextValue, currentValue));
}
免责声明:只知道您的 less-than-real-world 基准可能没有显示全貌,但我不会在这里深入讨论。
我们可以非常初步地了解一下 JavaScript 引擎是如何调用函数的。请注意,我不是 JS 运行时如何工作的专家,所以请纠正我错误或不完整的地方。
每当执行一个函数时,都会创建一个“调用范围”并将其添加到堆栈中。对于 normal/classical 函数(non-arrow 函数),引擎会创建一个新的上下文,它有自己的“闭包”和变量范围。在此上下文中有一些隐式创建的变量,例如引擎放置在那里的 this
和 arguments
。
function foo() {
const self = this;
function bar(...args) {
console.log(this === self); //-> false
console.log(arguments); //-> { length: 1, 0: 'barg' }
console.log(args); //-> [ 'barg' ]
}
bar('barg');
console.log(arguments); //-> { length: 1, 0: 'farg' }
}
foo('farg');
箭头函数的工作方式与常规函数非常相似,但没有额外的闭包和额外的变量。密切关注日志结果的差异:
function foo() {
const self = this;
const bar = (...args) => {
console.log(this === self); //-> true
console.log(arguments); //-> { length: 1, 0: 'farg' }
console.log(args); //-> [ 'barg' ]
}
bar('barg');
console.log(arguments); //-> { length: 1, 0: 'farg' }
}
foo('farg');
有了这个非常热门的……几乎是肤浅的……知识,您可以看到引擎在创建箭头函数时正在做“更少的工作”。有理由认为,箭头函数本质上因此更快。 此外,我认为箭头函数不会比常规函数 () 引入更多的内存泄漏或垃圾收集可能性。
编辑:
值得一提的是,每次声明一个函数时,您都在定义一个占用 space 内存的新变量。 JavaScript 引擎还必须为每个函数保留“上下文”,以便在“父”作用域中定义的变量在函数执行时仍然可用。例如,任何时候你在上面调用 foo
,都会在内存中创建一个新的 bar
变量,可以访问其父“foo”调用范围的完整上下文。 JS 引擎擅长确定何时不再需要上下文,并会在垃圾收集期间清理它,但要知道如果有很多垃圾,即使垃圾收集也会很慢。
此外,引擎有一个优化层(参见 Turbofan),它非常智能并且不断发展。为了说明这一点,请考虑您提供的示例:
function doSomething() {
const someUpperContextValue = 5;
[1, 2, 3].map((currentValue) => calculateValues(someOutterContextValue, currentValue));
}
在不太了解 JS 引擎内部的情况下,我可以看到引擎优化了 doSomething
函数,因为 someUpperContextValue
变量的静态值为 5
。如果将该值更改为 Math.random()
之类的值,现在引擎不知道该值是什么,也无法优化。出于这些原因,许多人会告诉您问“哪个更快”是在浪费时间,因为您永远不知道什么时候一个无害的小变化会完全破坏您的性能。
我正在从事一个每天处理数十亿个请求的项目,我非常关心性能和内存使用情况。
昨天我对每分钟执行很多很多次的代码进行了一些更改,并且我使用箭头函数进行一些映射,但是团队要求我在使用 .map 时始终使用 argsThis,但我没有'不明白为什么,所以我做了很多基准测试,结果正好相反。
Benchmark(绑定 vs 箭头函数 vs ArgsThis)
他们的论点是,垃圾回收在使用箭头函数时是最糟糕的,并且这些案例没有显示真实场景,因为它的上下文非常浅。
基准测试显示箭头函数要快得多,有什么我需要考虑的吗?
谢谢!
编辑:
问题是我们需要来自上层上下文的变量的情况,例如:
function doSomething() {
const someUpperContextValue = 5;
[1, 2, 3].map((currentValue) => calculateValues(someOutterContextValue, currentValue));
}
免责声明:只知道您的 less-than-real-world 基准可能没有显示全貌,但我不会在这里深入讨论。
我们可以非常初步地了解一下 JavaScript 引擎是如何调用函数的。请注意,我不是 JS 运行时如何工作的专家,所以请纠正我错误或不完整的地方。
每当执行一个函数时,都会创建一个“调用范围”并将其添加到堆栈中。对于 normal/classical 函数(non-arrow 函数),引擎会创建一个新的上下文,它有自己的“闭包”和变量范围。在此上下文中有一些隐式创建的变量,例如引擎放置在那里的 this
和 arguments
。
function foo() {
const self = this;
function bar(...args) {
console.log(this === self); //-> false
console.log(arguments); //-> { length: 1, 0: 'barg' }
console.log(args); //-> [ 'barg' ]
}
bar('barg');
console.log(arguments); //-> { length: 1, 0: 'farg' }
}
foo('farg');
箭头函数的工作方式与常规函数非常相似,但没有额外的闭包和额外的变量。密切关注日志结果的差异:
function foo() {
const self = this;
const bar = (...args) => {
console.log(this === self); //-> true
console.log(arguments); //-> { length: 1, 0: 'farg' }
console.log(args); //-> [ 'barg' ]
}
bar('barg');
console.log(arguments); //-> { length: 1, 0: 'farg' }
}
foo('farg');
有了这个非常热门的……几乎是肤浅的……知识,您可以看到引擎在创建箭头函数时正在做“更少的工作”。有理由认为,箭头函数本质上因此更快。 此外,我认为箭头函数不会比常规函数 (
编辑:
值得一提的是,每次声明一个函数时,您都在定义一个占用 space 内存的新变量。 JavaScript 引擎还必须为每个函数保留“上下文”,以便在“父”作用域中定义的变量在函数执行时仍然可用。例如,任何时候你在上面调用 foo
,都会在内存中创建一个新的 bar
变量,可以访问其父“foo”调用范围的完整上下文。 JS 引擎擅长确定何时不再需要上下文,并会在垃圾收集期间清理它,但要知道如果有很多垃圾,即使垃圾收集也会很慢。
此外,引擎有一个优化层(参见 Turbofan),它非常智能并且不断发展。为了说明这一点,请考虑您提供的示例:
function doSomething() {
const someUpperContextValue = 5;
[1, 2, 3].map((currentValue) => calculateValues(someOutterContextValue, currentValue));
}
在不太了解 JS 引擎内部的情况下,我可以看到引擎优化了 doSomething
函数,因为 someUpperContextValue
变量的静态值为 5
。如果将该值更改为 Math.random()
之类的值,现在引擎不知道该值是什么,也无法优化。出于这些原因,许多人会告诉您问“哪个更快”是在浪费时间,因为您永远不知道什么时候一个无害的小变化会完全破坏您的性能。