1

I am wondering why these code variations are behaving the way they do.

First Setup:

var a = {

  val: 23,

  div: null,

  test: function(){

    this.div.innerHTML = this.div.innerHTML + ' ' + this.val;

  },

  run: function(){

    // why is "this" the window object here when called by requestAnimationFrame?

    requestAnimationFrame(this.run);

    this.test();

  },

  init: function(){

    this.div = document.getElementById('output');

    this.run();

  }

};

a.init();

This one I expected to work but fails as soon as requestAnimationFrame begins calling the run function as this refers to Window when called by requestAnimationFrame. Fiddle: http://jsfiddle.net/titansoftime/gmxourcq/

This one works:

var a = {

    val: 23,

    div: null,

    test: function(){

        this.div.innerHTML = this.div.innerHTML + ' ' + this.val;

    },

    run: function(){

        var self = this;

        requestAnimationFrame(function(){ self.run(); });

        this.test();

    },

    init: function(){

      this.div = document.getElementById('output');

      this.run();

    }

};

a.init();

Passing the run function via a closure solves the problem. Why would this work and not the prior example work? My actual code needs to run extremely efficiently and I'd rather not use a closure inside my run loop as I don't want to poke the GC. Perhaps this is not a big deal? (I'm mostly worried about mobile devices). Fiddle: http://jsfiddle.net/titansoftime/Lqhcwoyu/

And finally the last case which works as expected:

var a = {

    val: 23,

    div: null,

    test: function(){

        this.div.innerHTML = this.div.innerHTML + ' ' + this.val;

    },

    run: function(){

        requestAnimationFrame(a.run);

        a.test();

    },

    init: function(){

      this.div = document.getElementById('output');

      this.run();

    }

};

a.init();

Again for performance (OCD) reasons I'd rather not reference this object via global if I can avoid it. Fiddle: http://jsfiddle.net/titansoftime/5816fLxe/

Can someone please explain to me why the first example fails and also the best course to take regarding running this loop as memory/GC efficient as possible?

Thank you for your time.

isherwood
  • 58,414
  • 16
  • 114
  • 157
Hobbes
  • 781
  • 10
  • 29

1 Answers1

2

The function reference that gets passed to requestAnimationFrame is executed in global context (where this refers to the window object, in the browser).

In order to execute the function reference passed to requestAnimationFrame in any other scope, you need to either bind the function reference to that scope:

requestAnimationFrame(this.run.bind(this));

or pass in an anonymous wrapper function which essentially executes your function in it's original scope (which is what you've done here).

var self = this;
requestAnimationFrame(function(){ self.run(); });
Adam Jenkins
  • 51,445
  • 11
  • 72
  • 100
  • Excellent thank you. Does one method have a benefit over the other performance wise? – Hobbes Mar 09 '15 at 16:12
  • 1
    the `bind` method essentially does the same thing as the anonymous function. A ridiculously simple test case says the Chrome's V8 prefers the anonymous ever so slightly (http://jsperf.com/raf-bind-vs-anonymous). I wouldn't worry about it either way, they're both very readable lines of code. – Adam Jenkins Mar 09 '15 at 16:22
  • Once again, thank you =] – Hobbes Mar 09 '15 at 16:23