6

class App {
  constructor() {
    this.canvas = document.createElement('canvas');
    document.body.appendChild(this.canvas);
    this.ctx = this.canvas.getContext('2d');

    this.pixelRatio = window.devicePixelRatio > 1 ? 2 : 1;

    window.addEventListener('resize', this.resize.bind(this), false);
    this.resize();

    window.requestAnimationFrame(this.animate);
  }

  resize() {
    this.stageWidth = document.body.clientWidth;
    this.stageHeight = document.body.clientHeight;
  }

  animate = () => {
    this.test(); // ---> here!
  };

  test = () => {
    console.log('here!');
  };
}

window.onload = () => {
  new App();
};

An arrow function is not hoisted, only regular functions are hoisted. How come, inside animate function, can call this.test? Different behavior of arrow function in a class?

AbsoluteBeginner
  • 2,160
  • 3
  • 11
  • 21
facVV
  • 379
  • 1
  • 3
  • 4

1 Answers1

7

Although arrow functions aren't hoisted, what you have here aren't just arrow functions - you're using class fields here, which are syntax sugar for assigning a value to the instance inside the constructor (at the beginning of the constructor, just after any super calls). Your code is equivalent to:

class App {
  constructor() {
    this.animate = () => {
      this.test(); // ---> here!
    };

    this.test = () => {
      console.log('here!');
    };
    this.canvas = document.createElement('canvas');
    // ...
  }
}

It's not an issue of hoisting.

First this.animate gets a function assigned to it. Then this.test gets a function assigned to it. Then, eventually, after a requestAnimationFrame, this.animate is called.

For a more minimal example of this:

const fn1 = () => {
  fn2();
};
const fn2 = () => {
  console.log('fn2');
};

fn1();

As long as the line that assigns the function to the variable has run before the function gets called, everything should work.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • ``` videoElem.addEventListener('canplaythrough', render); const render = () => { ctx.drawImage(videoElem, 0, 0, 600, 400); ctx.font = '20px Bungee Outline'; ctx.fillStyle = 'white'; } ``` in this case, i got an error - then why is that? – facVV Dec 30 '20 at 18:54
  • 1
    @facVV Because, as CertainPerformance explained, arrow functions aren't hoisted (in the sense you mean), and what you see in the class is a different thing. However, this example has no class in it at all, so even if they may look similarly, they're different. For this example, refer to [TDZ (Temporal Dead Zone)](https://stackoverflow.com/q/33198849/8376184) – FZs Dec 31 '20 at 14:36
  • @CertainPerformance If I'm right, the problem with the code in facVV's comment is that the `render` variable (that contains an arrow function) is referenced earlier than it's defined... – FZs Dec 31 '20 at 14:40
  • @facVV You can't run a line that references a variable before the line that has initialized the line (with `const someVarName =`) has run. – CertainPerformance Dec 31 '20 at 15:00