箭头函数是否比 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 中)?这种惩罚真的存在吗?

下图表明:

  1. 先走会受到惩罚(传统或肥胖)
  2. Chrome
  3. 没有明显区别

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,您会注意到函数 setColorgetColor 创建的品牌每个实例都是新的,每个新副本都放在每个实例上,而函数 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 中做了一个简短的基准测试。我 运行 很多次似乎箭头函数在大多数情况下比普通函数快一点点。即使是一次或两次正常功能也更快......看起来差异微不足道。所以简而言之 - 如果您不需要介意 contextthis,只需使用对您来说更好的任何东西 ;)

https://jsben.ch/kJxPT

const a = (b, c) => b+c;

a(1,2);

VS

function a(b,c){
    return b+c;
}

a(1,2);