箭头函数是否比 v8 中的普通独立函数声明更快(性能更高、重量更轻)?

IT技术 javascript node.js v8
2021-02-16 13:15:19

我问这个问题是因为我和我的同事在编码风格上有争议,因为他更喜欢箭头函数声明:

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

我更喜欢旧式的独立函数声明:

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

我的观点是旧式代码更具可读性,您可以更清楚地区分函数和变量声明。他的观点是带有箭头函数的代码运行得更快。

当您使用旧式独立函数声明而不是箭头函数时,您是否了解实际性能损失(在 v8 中)?这种惩罚真的存在吗?

6个回答

V8 开发人员在这里。箭头函数(大部分)只是传统函数声明的“语法糖”。没有性能差异。

@Matheus 这意味着当箭头函数使用时this,它需要创建一个闭包。没什么了不起的。此外,我希望在大多数情况下可以优化函数对象的创建,因此不必创建.prototype对象也可能没有任何好处
2021-04-16 13:15:19
@NormanXu:箭头函数的实现不在幕后使用.bind(),所以没有“原始函数”来包装:箭头函数原始函数
2021-04-19 13:15:19
我不确定你在问什么——口译是一种执行方式。无论哪种方式,“没有性能差异”都涵盖了它。
2021-04-26 13:15:19
箭头函数表达式不仅仅是语法糖。Function顺便说一下,他们基本上仍然创建对象。我从未阅读过 ES6.0 规范。完全(仅 ES3.0)所以我说的可能不准确,但是:调用Function由箭头函数表达式构造的 athis在其执行上下文中会导致不同的值(通常说是this来自作用域链值)呼叫者)。
2021-05-03 13:15:19
@Matheus:是的,围绕 存在语义差异this,这就是我写“(大部分)”的原因。这个问题(和我的回答)的重点是没有性能差异——就像你说的,在引擎盖下它们是相同的Function对象,这就是为什么我认为将它们称为“语法糖”是公平的。-- @Bergi:不幸的是,能够优化掉函数对象的创建是例外,而不是规则,因为 JavaScript 中的很多东西都是可以观察到的。由于这个原因,封闭密集型程序往往内存效率很低。
2021-05-10 13:15:19

以下表明:

  1. 先行有惩罚(传统的或胖的)
  2. 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>

好的。该规范定义了创建这两种函数所采取的步骤。也可能有某种手指在空中“这做得更多”。
2021-04-19 13:15:19
@BenAston 我只是希望一些具有深厚 v8 知识的大师会看到我的问题并给出客观的答案。:)
2021-05-01 13:15:19
很公平,但如果你不得不担心这个,你不是已经回答了你的问题吗?如果在实践中无法检测到差异,那么询问理论就没有实际意义(除非您对力学差异感兴趣,这不是您所问的)。
2021-05-03 13:15:19
有一个巨大的惩罚,因为你在按钮被点击之前启动了第一个计时器......
2021-05-05 13:15:19
当你不知道 v8 的幕后发生了什么(我真的不知道我读过 v8 的源代码,但我无论如何都不专业)你不能因为 v8 以正确的方式解释结果优化。查看本文以获取一些示例mrale.ph/blog/2012/12/15/microbenchmarks-fairy-tale.html
2021-05-07 13:15:19

我认为类属性中的箭头函数可能会导致一些性能问题。这是一个例子:

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 驻留在原型上。

如果你想让一千个实例每个都能够进行固定上下文的方法引用,你将需要一千个单独的方法(不是一个共享的),当然然后你将不得不存储这千个中的每一个实例本身上的单独方法,从而破坏了单个共享原型的全部意义。

我真的很想看到有人通过测试跟进这个。
2021-04-27 13:15:19

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$ 节点 test_plus_i.js

脂肪 [ 0, 931986419 ]

箭头 [ 0, 960479009 ]

bash-3.2$ 节点 test_zero.js

脂肪 [ 0, 4795578​​88 ]

箭头 [ 0, 478563661 ]

bash-3.2$ 节点 --version

v12.8.0

bash-3.2$

所以你可以看到函数调用开销没有区别。

这是对问题的一个很好的回答。绑定 this 的开销可能很小。但是,它需要多次运行,并且还需要以相反的顺序运行测试循环。没有人知道 gc 背景是怎么回事并弄乱了结果..
2021-05-02 13:15:19

在我的 exp 中,我发现箭头函数确实比普通的 JS 函数运行得更快。这是 react 中的一个小片段,它使用了箭头和普通函数。我发现使用箭头函数的组件比使用普通 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'))

如果您的意见不同,请告诉我