var self = this?
Asked Answered
A

8

196

Using instance methods as callbacks for event handlers changes the scope of this from "My instance" to "Whatever just called the callback". So my code looks like this

function MyObject() {
  this.doSomething = function() {
    ...
  }

  var self = this
  $('#foobar').bind('click', function(){
    self.doSomethng()
    // this.doSomething() would not work here
  })
}

It works, but is that the best way to do it? It looks strange to me.

Ade answered 3/12, 2008 at 16:55 Comment(6)
You should avoid self as there is a window.self object and you could end up using that accidentally if you forget to declare your own self var (e.g. when moving some code around). This can be annoying to spot/debug. Better to use something like _this.Wendolynwendt
related: What does var that = this; mean in javascript?Hub
From the JavaScript Tutorial: "The value of this is dynamic in JavaScript. It is determined when function is called, not when it is declared."Gooden
Possible duplicate of: What underlies this JavaScript idiom: var self = this?Gooden
The thing is, in the global scope self === this. Therefore, self in local contexts makes sense and is following the pattern.Australasia
A great explanation on mysterious this behavior based on context hereOersted
M
218

This question is not specific to jQuery, but specific to JavaScript in general. The core problem is how to "channel" a variable in embedded functions. This is the example:

var abc = 1; // we want to use this variable in embedded functions

function xyz(){
  console.log(abc); // it is available here!
  function qwe(){
    console.log(abc); // it is available here too!
  }
  ...
};

This technique relies on using a closure. But it doesn't work with this because this is a pseudo variable that may change from scope to scope dynamically:

// we want to use "this" variable in embedded functions

function xyz(){
  // "this" is different here!
  console.log(this); // not what we wanted!
  function qwe(){
    // "this" is different here too!
    console.log(this); // not what we wanted!
  }
  ...
};

What can we do? Assign it to some variable and use it through the alias:

var abc = this; // we want to use this variable in embedded functions

function xyz(){
  // "this" is different here! --- but we don't care!
  console.log(abc); // now it is the right object!
  function qwe(){
    // "this" is different here too! --- but we don't care!
    console.log(abc); // it is the right object here too!
  }
  ...
};

this is not unique in this respect: arguments is the other pseudo variable that should be treated the same way — by aliasing.

Magritte answered 3/12, 2008 at 17:58 Comment(4)
You really should bind "this" to the function call and not pass it around or traverse variable scopes with itRizal
@Rizal Why is that?Thermo
@Rizal you might be working inside pipe, map, subscribes... in one function and not calling functions, in this case "this" is changing, so the variable scope is the solutionSplendor
@SkanderJenhani that comment is 8 years old. My answer would be very different todayRizal
D
19

Yeah, this appears to be a common standard. Some coders use self, others use me. It's used as a reference back to the "real" object as opposed to the event.

It's something that took me a little while to really get, it does look odd at first.

I usually do this right at the top of my object (excuse my demo code - it's more conceptual than anything else and isn't a lesson on excellent coding technique):

function MyObject(){
  var me = this;

  //Events
  Click = onClick; //Allows user to override onClick event with their own

  //Event Handlers
  onClick = function(args){
    me.MyProperty = args; //Reference me, referencing this refers to onClick
    ...
    //Do other stuff
  }
}
Driveway answered 3/12, 2008 at 17:11 Comment(0)
W
15

If you are doing ES2015 or doing type script and ES5 then you can use arrow functions in your code and you don't face that error and this refers to your desired scope in your instance.

this.name = 'test'
myObject.doSomething(data => {
  console.log(this.name)  // this should print out 'test'
});

As an explanation: In ES2015 arrow functions capture this from their defining scope. Normal function definitions don't do that.

Widower answered 12/9, 2016 at 22:39 Comment(2)
As an explanation: In ES2015 arrow functions capture this from their defining scope. Normal function definitions don't do that.Ade
@Ade I don't think it's anything special. Functions create a block scope. so using arrow notation instead of a function keeps you from creating a scope, so, this will be still referring to the outer this variable.Ennui
I
12
var functionX = function() {
  var self = this;
  var functionY = function(y) {
    // If we call "this" in here, we get a reference to functionY,
    // but if we call "self" (defined earlier), we get a reference to function X.
  }
}

edit: in spite of, nested functions within an object takes on the global window object rather than the surrounding object.

Innocent answered 26/4, 2015 at 18:7 Comment(2)
You seem to be describing what var self = this does, but that isn't what the question is asking (the question is asking if that is the best solution to the problem).Irreparable
" The core problem is how to "channel" a variable in embedded functions." I agree this statement in the accepted answer. Besides that there is nothing wrong with "that" practice. It is pretty common. So,as an answer to "code looks strange to me" part i chose to explain how it works. I believe the reason that the "code looks strange to me" comes from the confusion of how it works.Innocent
T
4

One solution to this is to bind all your callback to your object with javascript's bind method.

You can do this with a named method,

function MyNamedMethod() {
  // You can now call methods on "this" here 
}

doCallBack(MyNamedMethod.bind(this)); 

Or with an anonymous callback

doCallBack(function () {
  // You can now call methods on "this" here
}.bind(this));

Doing these instead of resorting to var self = this shows you understand how the binding of this behaves in javascript and doesn't rely on a closure reference.

Also, the fat arrow operator in ES6 basically is the same a calling .bind(this) on an anonymous function:

doCallback( () => {
  // You can reference "this" here now
});
Trichromatic answered 1/6, 2016 at 18:55 Comment(1)
Personally I think .bind(this) is just more typing, changing the this reference to me (or whatever) and then just using it instead makes for cleaner code. Fat arrow is the future tho.Rowdy
C
3

I haven't used jQuery, but in a library like Prototype you can bind functions to a specific scope. So with that in mind your code would look like this:

 $('#foobar').ready('click', this.doSomething.bind(this));

The bind method returns a new function that calls the original method with the scope you have specified.

Coursing answered 3/12, 2008 at 17:16 Comment(2)
Analogous to: $('#foobar').ready('click', this.doSomething.call(this)); right?Hodgkins
The bind method doesn't work in older browsers, so use the $.proxy method instead.Grouch
F
2

Just adding to this that in ES6 because of arrow functions you shouldn't need to do this because they capture the this value.

Fabrin answered 29/10, 2015 at 21:56 Comment(0)
S
1

I think it actually depends on what are you going to do inside your doSomething function. If you are going to access MyObject properties using this keyword then you have to use that. But I think that the following code fragment will also work if you are not doing any special things using object(MyObject) properties.

function doSomething(){
  .........
}

$("#foobar").ready('click', function(){

});
Schexnayder answered 3/12, 2008 at 17:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.