54
\$\begingroup\$

I was curious which way is generally preferred, or if there is even a preference, given these two options:

$.ajax({
    url: '../Component/GetSearchFilters',
    success: function (response) {
        console.log("Outer scope:", this);
    }.bind(this)
});

If I was inside of a function where I needed both "this" and outer-scope's "this," then I would assign a variable, but I generally prefer binding to keep variable usage scoped to as few lines as possible.

\$\endgroup\$
1
  • \$\begingroup\$ While this question was originally closed as "unclear" (presumably for lack of context), it should be noted that by today's standards it would be closed as "hypothetical" code, and further flags will be declined. \$\endgroup\$ Commented Sep 15, 2015 at 17:32

6 Answers 6

42
\$\begingroup\$

Things that would favor var self = this;

  • bind isn't supported in IE8 and Safari5. If you aim to build a library or code that supports legacy browsers, then var self = this would be more cross-browser friendly.

  • Sometimes, callbacks are bound to a certain context when executed like in the case of jQuery's $.each where this is the current item. If you don't want to lose this context but still want to have the parent context, then it's another reason to use var self = this;.

  • Calling bind creates a brand new function with a forced context. This function creation step might be a performance issue in the large scale especially if you call bind inside a loop.

Reasons that would favor bind

  • Performance wouldn't be an issue if you just created a bound function once and reused it in several places. Like you can create a bound function and call that bound function in a loop (instead of calling bind in a loop).

  • bind also alleviates you from doing call(customContext) or apply(customContext) explicitly all the time.

  • Scope lookup (looking for variables if not found in the current scope) can pose a performance problem especially in very deep scopes. You've probably heard of the tip "passing to local is better".

I'm not saying bind is bad (I use them extensively myself). It just needs to be used in the right situations. Also, the performance issues for both might be so negligible, they can only be seen if you aim to do stuff in less than 16ms (do stuff in 60fps).

\$\endgroup\$
7
  • 2
    \$\begingroup\$ Note that the first can be solved with es5-shim. Using self requires a closure for the callback, but I haven't investigated a) if there's no closure without it and b) how much of an impact that has. \$\endgroup\$ Commented May 15, 2014 at 20:29
  • 1
    \$\begingroup\$ It all depends on what the code is doing and the js engine but I would guess that the closure of this to be a more expensive operation. IMO you should just use what you feel is best and not worry about micro optimizations. \$\endgroup\$
    – pllee
    Commented May 15, 2014 at 21:46
  • \$\begingroup\$ @pllee I believe that the function returned by bind is a closure as well, so this isn't really a mark against var self = this. \$\endgroup\$ Commented May 16, 2014 at 4:59
  • \$\begingroup\$ After doing some research the closure is much faster than bind currently in most js engines, the overhead is still minimal and I would stick to what you prefer. \$\endgroup\$
    – pllee
    Commented May 16, 2014 at 14:11
  • \$\begingroup\$ @pllee You are right. Self closure is faster than bind: jsperf.com/bind-vs-self-closure \$\endgroup\$ Commented Apr 11, 2015 at 7:45
14
\$\begingroup\$

Just to add another alternative, instead of doing

var self = this;
something( function () {
    // callback stuff
} );

one can also do this:

( function( self ) {
    something( function () {
        // callback stuff
    } );
} )( this );

Is it better? It might depend. The downsides are another scope and a deeper level of indentation.

One upside is that it's self-contained and the new scope avoids cluttering up the surriounding scope, though you can argue that in well-designed code there shouldn't be too much code in one scope anyway.

One could also say it's not too readable, though I personally would argue that bind( this ) really is that much more readable.

I just wanted to point out another option as having more options to choose from is generally a good thing.

\$\endgroup\$
0
6
\$\begingroup\$

Simple rule of thumb use bind if you don't need to have a reference to the original this context. If the original this context is needed use a self variable obviously.

 $.ajax({
    url: '../Component/GetSearchFilters',
    success: function (response) {
        console.log("Outer scope:", this);
    }.bind(this)
});

vs

var me = this;
$.ajax({
    url: '../Component/GetSearchFilters',
    success: function (response) {
        console.log("Outer scope:", me);
    }
});

Using bind is slightly more consise and easier to read than the me variable but one way over the other really isn't a big deal.

\$\endgroup\$
0
6
\$\begingroup\$

One thing to note about self specifically; by default, self is an alias for window. If you override that with a var self, you're fine, but if you forget to add that line and you use self in an inner scope assuming it's referring to your outer scope, there's a potential for some nasty bugs that can be hard to track down.

For that reason, I tend to use some other identifier: that, _this, some variable denoting what type it is, such as app if it's referring to something I'm calling an "application", view if it's a view, etc. In this case, as long as you're in strict mode or properly linting your code to object to using undefined variables, you'll get notified if you forget to add that alias.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ I tend to use me, but I agree that it's better to use a name that actually means something. \$\endgroup\$ Commented Oct 10, 2014 at 9:36
  • \$\begingroup\$ I use theThis, LMAO \$\endgroup\$
    – BigName
    Commented Mar 20, 2017 at 14:26
2
\$\begingroup\$

Depending upon what you are doing, using bind will be a little slower or a bunch slower. I found myself asking this question when pondering if it were better to close over a set of variables, or pass and bind them as arguments (they never change).

Here is the code I wrote to test this out. You can change the values of I and J to adjust the loops. When it comes to actual execution, they are comparable in speed (the closure was a little bit faster). Unless of course the optimizer gets to it and then the closure is about 4 times faster. However, instantiation is about 2-3 times slower with bind.

These tests were performed in Firefox 33

function q(a, b){
  var a = {a:a}, b = {b:b};
  return {
    p:function(){ return a.a + b.b; },
    s:function(){ return a.a - b.b; },
    m:function(){ return a.a * b.b; },
    d:function(){ return a.a / b.b; },
    get_a:function(){ return a.a; },
    get_b:function(){ return b.b; },
    set_a:function(v){ a.a = v; },
    set_b:function(v){ b.b = v; },
  };
}

var I = 1000, J = 100;
var i, j;
var t0 = performance.now()
for(i = 0; i < I; i++){
  var k = new q(5, 6);
  for(j = 0; j < J; j++){
    k.p() + k.s() + k.m() + k.d();
  }
}
var t1 = performance.now();

document.write(t1 - t0)
document.write("<br/>")

var p;

(function(){
  function abp(a,b){ return a.a + b.b; }
  function abs(a,b){ return a.a - b.b; }
  function abm(a,b){ return a.a * b.b; }
  function abd(a,b){ return a.a / b.b; }
  function ga(a){ return a.a; }
  function gb(b){ return b.b; }
  function sa(a,v){ a.a = v; }
  function sb(b,v){ b.b = v; }
  function w(a, b){
    var a = {a:a}, b = {b:b};
    var m = {};
    m.p = abp.bind(m,a,b);
    m.s = abs.bind(m,a,b);
    m.m = abm.bind(m,a,b);
    m.d = abd.bind(m,a,b);
    m.get_a = ga.bind(m,a);
    m.get_b = gb.bind(m,b);
    m.set_a = sa.bind(m,a);
    m.set_b = sb.bind(m,b);
    return m;
  }
  p = w;
})();

var t2 = performance.now()
for(i = 0; i < I; i++){
  var k = new p(5, 6);
  for(j = 0; j < J; j++){
    k.p() + k.s() + k.m() + k.d();
  }
}
var t3 = performance.now();

document.write(t3 - t2)

TL;DR: .bind is slower.

\$\endgroup\$
2
  • \$\begingroup\$ This is a very shortsighted test. You are executing the functions in the same scope in which you delcared them, which sort of defeats the entire purpose of both of these approaches. If you want to do a real performance test, do one where you're passing the function to a far away scope. This is more realistic, since this is when most people would be using one of these approaches, and more fair, since the cost of grabbing a value referenced through closure will be much greater. \$\endgroup\$
    – Dogs
    Commented Aug 22, 2015 at 1:25
  • \$\begingroup\$ @Dogs, A Real Performance Test would involve testing the performance of a Real piece of software, not a contrived test script. I would argue that ANY short script I write would be contrived and not necessarily representative. I'm unsure how to create a "far away scope," how do you measure scope distance? \$\endgroup\$ Commented Apr 21, 2016 at 2:14
1
\$\begingroup\$

Since no one has mentioned it yet. jQuery's proxy - a working cross browser solution:

$.ajax({
    url: '/echo/json/',
    success: $.proxy(function(){
        console.log(this === window); // true
    }, this)
});

this would normally refer to the jqXhr object in the success callback.

http://jsfiddle.net/thLhh54q/

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.