Reputation: 495
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
}
Upvotes: 5
Views: 503
Reputation: 89
Recently I ran across this article about this.
In section 4.2, he uses an example similar to your code and highlights the pitfall of forgetting 'new'. 'this' in a function invocation points to the global object (window or browser) so when you return it, you put your functions onto the global object and then return a reference to it. If you would have used new Unit() and don't return this, you would get an object with your functions on it.
You could use bind, but I think it would be something like Unit.bind(Unit) which would be look weird.
You could also use a factory function to return the object and you won't have to worry about forgetting new
function Unit(){
var move = function(direction){
switch(direction){
case 'up': { console.log('foo'); break; }
case 'down': { console.log('foooo'); break; }
}
console.log('bar');
};
var shoot = function(){console.log('zap')};
var actions = {
moveUp : function(){ move('up') },
moveDown : function(){ move('down') },
shoot : shoot
};
return {
actions:actions
};
}
var player1=Unit();
player1.actions.shoot();
player1.actions.moveDown();
player1.actions.moveUp();
I removed the this from the call to move, part of the problem is actions is an object not a function, so while you could use bind on the functions inside the object, you can as I have in the code, just close over the functions and then expose it.
Also if you use the self=this and don't use the new keyword, you still have reference to the window/browser object and are creating global scope.
Upvotes: 0
Reputation: 5606
Another solution: this
is not a variable. Nested function have access to all variables defined in "parent" function (clousure) -- you can create variable, assign this
to it, and use it instead of this
, and it'll do what you want and expect:
function Unit(){
var self = this;
self.move = function(direction){
switch(direction){
case 'up': { console.log('foo'); break; }
case 'down': { console.log('foooo'); break; }
}
console.log('bar');
}
self.shoot = function(){console.log('zap')}
self.actions = {
'moveUp' : function(){ self.move('up') },
'moveDown' : function(){ self.move('down') },
'shoot' : function(){ self.shoot() }
}
return self
}
Upvotes: 1
Reputation: 1519
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.
Upvotes: 2
Reputation: 6803
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
}
Upvotes: 2