Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope of "this" within object

Tags:

javascript

This (below) ended up giving me a "maximum call stack size exceeded" error. It seems like it's due to the way "this" is being interpreted within the "this.actions" object. Within that object, does "this" refer to that object, or the instance of the Unit class? If the former, would putting a .bind(this) on the end of the "this.actions" object make "this" refer to the class instance instead? If so, why? If not, why not?

function Unit(){
  this.move = function(direction){
    switch(direction){
      case 'up': { console.log('foo'); break; }
      case 'down': { console.log('foooo'); break; }
    }
    console.log('bar');
  }
  this.shoot = function(){console.log('zap')}

  this.actions = {
    'moveUp' : function(){ this.move('up') },
    'moveDown' : function(){ this.move('down') },
    'shoot' : function(){ this.shoot() }
  }

  return this
}
like image 943
DJG Avatar asked Jun 30 '16 16:06

DJG


2 Answers

The keyword this in the actions object will refer to the actions object.

Some possible fixes might look like:

function Unit(){
  var self = this;
  this.move = function(direction){
    switch(direction){
      case 'up': { console.log('foo'); break; }
      case 'down': { console.log('foooo'); break; }
    }
    console.log('bar');
  }
  this.shoot = function(){console.log('zap')}

  this.actions = {
    'moveUp' : function(){ this.move('up') }.bind(self),
    'moveDown' : function(){ this.move('down') }.bind(self),
    'shoot' : function(){ this.shoot() }.bind(self)
  }

  return this
}

Or, when you invoke those methods, you could use call or apply

eg:

var coolUnit = new Unit();
Unit.actions.moveUp.call(coolUnit);

Understanding this in the context of objects takes some work but here are some resources:

How does the "this" keyword work?

http://unschooled.org/2012/03/understanding-javascript-this/

http://javascriptissexy.com/understand-javascripts-this-with-clarity-and-master-it/

TL;DR - There are series of mental "rules" you can use to help keep track of what this is in a given context. Eg. The left-of-the-dot rule where the object to the left of the "dot" gets the this binding.

Object.foo() <- `this` in the method `foo` will point to `Object`

Using the "rule" mentioned above, you can rationalize that new Unit.actions.moveUp() would have the this binding set to point to the actions object because its left-of-the-dot.

Or you can use call/bind/apply to bind the this to the context you wish as shown above.

like image 88
The Dembinski Avatar answered Nov 03 '22 21:11

The Dembinski


Use bind

Reason:

lets say:

var r = new Unit();

When u call r.actions.moveup() , 'this' passed in the moveup function is actions.

function Unit(){
  this.move = function(direction){
    switch(direction){
      case 'up': { console.log('foo'); break; }
      case 'down': { console.log('foooo'); break; }
    }
    console.log('bar');
  }
  this.shoot = function(){console.log('zap')}

  this.actions = {
    'moveUp' : function(){ this.move('up') }.bind(this),
    'moveDown' : function(){ this.move('down') }.bind(this),
    'shoot' : function(){ this.shoot() }.bind(this)
  }

  return this
}
like image 21
Piyush.kapoor Avatar answered Nov 03 '22 21:11

Piyush.kapoor