箭头函数是否比 v8 中的普通独立函数声明更快(性能更高、更轻)?
Are arrow functions faster (more performant, lighter) than ordinary standalone function declaration in v8?
我问这个问题是因为我和我的同事在编码风格上有争议,因为他更喜欢箭头函数声明:
const sum = (a, b) => a + b;
我更喜欢老式的独立函数声明:
function sum(a, b) {
return a + b;
}
我的观点是,旧式代码更具可读性,您可以更清楚地区分函数和变量声明。他的观点是带有箭头函数的代码 运行 更快。
当您使用旧式独立函数声明而不是箭头函数时,您是否了解实际性能损失(在 v8 中)?这种惩罚真的存在吗?
下图表明:
- 先走会受到惩罚(传统或肥胖)
- Chrome
没有明显区别
function goFat() {
for (var i = 0; i < 1000000; i++) {
var v = ()=>{};
v();
}
}
function goTraditional() {
for (var i = 0; i < 1000000; i++) {
var v = function() {};
v();
}
}
function race() {
var start = performance.now();
goTraditional();
console.log('Traditional elapsed: ' + (performance.now() - start));
start = performance.now();
goFat()
console.log('Fat elapsed: ' + (performance.now() - start));
start = performance.now();
goTraditional();
console.log('Traditional elapsed: ' + (performance.now() - start));
start = performance.now();
goFat()
console.log('Fat elapsed: ' + (performance.now() - start));
console.log('------');
}
<button onclick="race()">RACE!</button>
这里是 V8 开发人员。箭头函数(大部分)只是 "syntactic sugar" 用于常规函数声明。没有性能差异。
在我的 exp 中,我发现箭头函数 运行 比普通的 JS 函数更快。
这是 React 中的一个小片段,它使用箭头和普通函数。我发现使用箭头函数的组件 运行s 比具有普通 js 函数的组件快一点。
https://codepen.io/lokeshpathrabe/pen/qgzadx
class Fun extends React.Component {
constructor(props){
super(props);
this.state = {start: new Date().getTime(),
end: new Date().getTime(),
number: 0};
console.log('Function start: ', this.state.start);
const fun = function(me){
let n = me.state.number
me.setState({
...me.state, end: new Date().getTime(), number: ++n
})
}
this.interval = setInterval(fun, 1, this);
}
stop(){
clearInterval(this.interval);
}
componentDidUpdate(){
if((this.state.end - this.state.start) > 5000){
console.log('Function end: ', this.state.end);
clearInterval(this.interval)
}
}
render() {
return (
<div>
<h2>Counter with Function {this.state.number}</h2>
</div>
)
}
}
class Arrow extends React.Component {
constructor(props){
super(props);
this.state = {start: new Date().getTime(),
end: new Date().getTime(),
number: 0};
console.log('Arrow start: ', this.state.start);
this.interval = setInterval(()=>{
let n = this.state.number
this.setState({
...this.state, end: new Date().getTime(), number: ++n
})
}, 1);
}
stop(){
clearInterval(this.interval);
}
componentDidUpdate(){
if((this.state.end - this.state.start) > 5000){
console.log('Arrow end: ', this.state.end);
clearInterval(this.interval)
}
}
render() {
return (
<div>
<h2>Counter with Arrow {this.state.number}</h2>
</div>
)
}
}
class HOC extends React.Component {
render() {
return (<div>
<h1>The one reaching higher count wins</h1>
<Arrow/>
<Fun/>
</div>);
}
}
ReactDOM.render(<HOC />, document.getElementById('react-content'))
如果您有不同意见,请告诉我
nodejs有两个例子:
function testFat(a, b) {
return a + b;
}
let testArrow = (a, b) => a + b;
let t1 = process.hrtime();
let tmp1 = 0;
for (let i = 0; i < 1000000000; ++i) {
tmp1 = testFat(tmp1, i);
}
var fatTime = process.hrtime(t1);
console.log('fat', fatTime);
let t2 = process.hrtime();
let tmp2 = 0;
for (let i = 0; i < 1000000000; ++i) {
tmp2 = testArrow(tmp2, i);
}
var arrowTime = process.hrtime(t2);
console.log('arrow', arrowTime);
function testFat() {
return 0;
}
let testArrow = () => 0;
let t1 = process.hrtime();
for (let i = 0; i < 1000000000; ++i) {
testFat();
}
var fatTime = process.hrtime(t1);
console.log('fat', fatTime);
let t2 = process.hrtime();
for (let i = 0; i < 1000000000; ++i) {
testArrow();
}
var arrowTime = process.hrtime(t2);
console.log('arrow', arrowTime);```
结果是:
bash-3.2$ node test_plus_i.js
fat [ 0, 931986419 ]
arrow [ 0, 960479009 ]
bash-3.2$ node test_zero.js
fat [ 0, 479557888 ]
arrow [ 0, 478563661 ]
bash-3.2$ node --version
v12.8.0
bash-3.2$
所以你可以看到函数调用开销没有区别。
我认为 class 属性中的箭头函数可能会导致一些性能问题。
这是一个例子:
class Car {
setColor = (color) => { this.color = color; }
constructor() {
this.color = '';
this.getColor = () => { return this.color; };
}
printCarColor() {
console.log(this.color);
}
}
var c = new Car();
console.log(c);
如果我们看一下变量 c,您会注意到函数 setColor 和 getColor 是 创建的品牌每个实例都是新的,每个新副本都放在每个实例上,而函数 printCarColor 驻留在原型上。
如果您希望一千个实例中的每一个都能够进行固定上下文方法引用,那么您将需要一千个单独的方法(而不是一个共享方法),当然您将不得不将数千个独立方法中的每一个都存储在实例本身上,从而破坏了单个共享原型的全部意义。
箭头函数只是一个函数表达式。以下相等:
const foo = (a, b) => a + b // foo = an anonymous function
const foo = function(a, b) { return a + b; }
const foo = new Function("a", "b", "return a + b")
一个函数声明可以被提升:
function foo(a, b) { return a + b; }
箭头函数不能用作生成函数,例如:
function* foo(a, b) {
yield a;
yield b;
}
当您的函数需要 this
关键字时考虑使用它们。
至少在性能上差别不大
我在 jsben.ch 中做了一个简短的基准测试。我 运行 很多次似乎箭头函数在大多数情况下比普通函数快一点点。即使是一次或两次正常功能也更快......看起来差异微不足道。所以简而言之 - 如果您不需要介意 context 或 this,只需使用对您来说更好的任何东西 ;)
const a = (b, c) => b+c;
a(1,2);
VS
function a(b,c){
return b+c;
}
a(1,2);
我问这个问题是因为我和我的同事在编码风格上有争议,因为他更喜欢箭头函数声明:
const sum = (a, b) => a + b;
我更喜欢老式的独立函数声明:
function sum(a, b) {
return a + b;
}
我的观点是,旧式代码更具可读性,您可以更清楚地区分函数和变量声明。他的观点是带有箭头函数的代码 运行 更快。
当您使用旧式独立函数声明而不是箭头函数时,您是否了解实际性能损失(在 v8 中)?这种惩罚真的存在吗?
下图表明:
- 先走会受到惩罚(传统或肥胖)
- Chrome 没有明显区别
function goFat() {
for (var i = 0; i < 1000000; i++) {
var v = ()=>{};
v();
}
}
function goTraditional() {
for (var i = 0; i < 1000000; i++) {
var v = function() {};
v();
}
}
function race() {
var start = performance.now();
goTraditional();
console.log('Traditional elapsed: ' + (performance.now() - start));
start = performance.now();
goFat()
console.log('Fat elapsed: ' + (performance.now() - start));
start = performance.now();
goTraditional();
console.log('Traditional elapsed: ' + (performance.now() - start));
start = performance.now();
goFat()
console.log('Fat elapsed: ' + (performance.now() - start));
console.log('------');
}
<button onclick="race()">RACE!</button>
这里是 V8 开发人员。箭头函数(大部分)只是 "syntactic sugar" 用于常规函数声明。没有性能差异。
在我的 exp 中,我发现箭头函数 运行 比普通的 JS 函数更快。 这是 React 中的一个小片段,它使用箭头和普通函数。我发现使用箭头函数的组件 运行s 比具有普通 js 函数的组件快一点。
https://codepen.io/lokeshpathrabe/pen/qgzadx
class Fun extends React.Component {
constructor(props){
super(props);
this.state = {start: new Date().getTime(),
end: new Date().getTime(),
number: 0};
console.log('Function start: ', this.state.start);
const fun = function(me){
let n = me.state.number
me.setState({
...me.state, end: new Date().getTime(), number: ++n
})
}
this.interval = setInterval(fun, 1, this);
}
stop(){
clearInterval(this.interval);
}
componentDidUpdate(){
if((this.state.end - this.state.start) > 5000){
console.log('Function end: ', this.state.end);
clearInterval(this.interval)
}
}
render() {
return (
<div>
<h2>Counter with Function {this.state.number}</h2>
</div>
)
}
}
class Arrow extends React.Component {
constructor(props){
super(props);
this.state = {start: new Date().getTime(),
end: new Date().getTime(),
number: 0};
console.log('Arrow start: ', this.state.start);
this.interval = setInterval(()=>{
let n = this.state.number
this.setState({
...this.state, end: new Date().getTime(), number: ++n
})
}, 1);
}
stop(){
clearInterval(this.interval);
}
componentDidUpdate(){
if((this.state.end - this.state.start) > 5000){
console.log('Arrow end: ', this.state.end);
clearInterval(this.interval)
}
}
render() {
return (
<div>
<h2>Counter with Arrow {this.state.number}</h2>
</div>
)
}
}
class HOC extends React.Component {
render() {
return (<div>
<h1>The one reaching higher count wins</h1>
<Arrow/>
<Fun/>
</div>);
}
}
ReactDOM.render(<HOC />, document.getElementById('react-content'))
如果您有不同意见,请告诉我
nodejs有两个例子:
function testFat(a, b) {
return a + b;
}
let testArrow = (a, b) => a + b;
let t1 = process.hrtime();
let tmp1 = 0;
for (let i = 0; i < 1000000000; ++i) {
tmp1 = testFat(tmp1, i);
}
var fatTime = process.hrtime(t1);
console.log('fat', fatTime);
let t2 = process.hrtime();
let tmp2 = 0;
for (let i = 0; i < 1000000000; ++i) {
tmp2 = testArrow(tmp2, i);
}
var arrowTime = process.hrtime(t2);
console.log('arrow', arrowTime);
function testFat() {
return 0;
}
let testArrow = () => 0;
let t1 = process.hrtime();
for (let i = 0; i < 1000000000; ++i) {
testFat();
}
var fatTime = process.hrtime(t1);
console.log('fat', fatTime);
let t2 = process.hrtime();
for (let i = 0; i < 1000000000; ++i) {
testArrow();
}
var arrowTime = process.hrtime(t2);
console.log('arrow', arrowTime);```
结果是:
bash-3.2$ node test_plus_i.js
fat [ 0, 931986419 ]
arrow [ 0, 960479009 ]
bash-3.2$ node test_zero.js
fat [ 0, 479557888 ]
arrow [ 0, 478563661 ]
bash-3.2$ node --version
v12.8.0
bash-3.2$
所以你可以看到函数调用开销没有区别。
我认为 class 属性中的箭头函数可能会导致一些性能问题。 这是一个例子:
class Car {
setColor = (color) => { this.color = color; }
constructor() {
this.color = '';
this.getColor = () => { return this.color; };
}
printCarColor() {
console.log(this.color);
}
}
var c = new Car();
console.log(c);
如果我们看一下变量 c,您会注意到函数 setColor 和 getColor 是 创建的品牌每个实例都是新的,每个新副本都放在每个实例上,而函数 printCarColor 驻留在原型上。
如果您希望一千个实例中的每一个都能够进行固定上下文方法引用,那么您将需要一千个单独的方法(而不是一个共享方法),当然您将不得不将数千个独立方法中的每一个都存储在实例本身上,从而破坏了单个共享原型的全部意义。
箭头函数只是一个函数表达式。以下相等:
const foo = (a, b) => a + b // foo = an anonymous function
const foo = function(a, b) { return a + b; }
const foo = new Function("a", "b", "return a + b")
一个函数声明可以被提升:
function foo(a, b) { return a + b; }
箭头函数不能用作生成函数,例如:
function* foo(a, b) {
yield a;
yield b;
}
当您的函数需要 this
关键字时考虑使用它们。
至少在性能上差别不大
我在 jsben.ch 中做了一个简短的基准测试。我 运行 很多次似乎箭头函数在大多数情况下比普通函数快一点点。即使是一次或两次正常功能也更快......看起来差异微不足道。所以简而言之 - 如果您不需要介意 context 或 this,只需使用对您来说更好的任何东西 ;)
const a = (b, c) => b+c;
a(1,2);
VS
function a(b,c){
return b+c;
}
a(1,2);