5

I know how to use the methods for changing the context of a function/method call: apply, bind, call. My question is this, is there a way to check if a function/method has already had its context set?

// given
var beta;

function alpha () {
  return this;
}

function isContextSet (fn) {
  /* check for context binding on fn */
}

beta = alpha.bind("hello");

isContextSet(alpha); // returns false
isContextSet(beta); // returns true

I think I know the answer to this already but thought I would ask anyway, if for no other reasons than: 1. confirm my assumption, or 2. learn something. I'm sure that I am not the first to ask this question but I have had no idea on how to actually find an answer since all I get are responses to how to use: .apply(), .call(), or .bind().

kalisjoshua
  • 2,306
  • 2
  • 26
  • 37
  • 1
    Maybe this helps: [What object javascript function is bound to (what is its “this”)?](http://stackoverflow.com/q/14307264/218196). – Felix Kling Jun 22 '13 at 13:40
  • 2
    _"has already had its context set"_ What do you mean by "already"? `.apply()`, `.call()` and `.bind()` don't permanently set `this` for every time the function gets called (noting that `.bind()` returns a _new_ function that permanently has a specific `this`, but doesn't actually change the _original_ function). Every possible way to call a function sets `this` to _something_ - it's just some ways do it implicitly, some do it explicitly... – nnnnnn Jun 22 '13 at 13:44
  • I added some code to show what I am try to get at. I understand how the three methods work, and I know how to check the context of a function/method while inside the body of the invocation. How do you check the context from outside the invocation, as I have shown above? I can't think of a way to do it at all in JS. – kalisjoshua Jun 22 '13 at 13:46
  • I think your right that it can't be done. After all, `.bind()` doesn't really "bind" anything; it just provides you with yet another function, which itself doesn't have `this` "pre-bound". – Pointy Jun 22 '13 at 13:48
  • 1
    In that case you should really look the question I linked to. It appears to be the same. – Felix Kling Jun 22 '13 at 13:58
  • 1
    [Felix Kling](http://stackoverflow.com/users/218196/felix-kling), you posted a great resource. I read through it, and it has a great suggestion. But I think that it is only solving a portion of the problem; albeit very well. I think what I am looking for is something that is of such minor importance that it isn't worth doing really since there would always be a way around it. There is no need to spend time attempting to solve such an edge case in this instance. This has been a great discussion though. Thank you everyone for joining in. – kalisjoshua Jun 22 '13 at 14:01

3 Answers3

3

No.

Actually, it's incorrect to refer to binding as "changing" the context. What it does is transform one function into another one which calls the first with a particular context.

Functions can be bound in many ways. For instance, I could bind it myself:

function bind(fn,ctxt){
    return function(){
        fn.apply(ctxt,arguments);
    };
}

Or, I could bind it with Function.prototype.bind, or a shim that does the same thing, or underscore's _.bind. I could bind it to initial arguments in addition to the context. In each case, there's no way to tell what has happened without essentially actually executing the function.

Binding is just one of many ways to transform one function into another one. I could also transform a function to execute one second from now, or be executed twice. There is nothing magic about binding; it just creates a new function based on the old one. There is no special variable called [[bound_to]] set on the function that you might be able to examine (unless you use your own custom implementation of bind, as another answer suggests). There's no post facto way to determine how the function was transformed.

The closest I can imagine coming is checking the possibly bound function against an unbound version and see if they are equal.

function log_this(){console.log(this);}
var bound_func=log_this.bind(my_var);
console.log(log_this===bound_func); // FALSE
  • 1
    I think you summarize this pretty well. I agree that there is no way to truly test the context, as I am defining above. JavaScript simply provide too many ways to get around the situation I am attempting to test for. – kalisjoshua Jun 22 '13 at 14:12
0

What could be done is something like this:

function f(context, whatever) {
    if(this !== context) {
        //context has been changed
    }
}

var y = f.bind(thingy);
y(otherThingy, something);

You could also have a custom bind function and have a sort of a hack like this:

function bind(func, context) {     
    var newFunc = func.bind(context);
    newFunc._this = context;
    return newFunc;
}

function checkContext(func, context) {
    return func._this !== context;
}
Jani Hartikainen
  • 42,745
  • 10
  • 68
  • 86
  • And here's a fiddle demonstrating my own version of a custom bind function, which I was about to post as an answer when I saw you'd already covered it with a similar idea: http://jsfiddle.net/jx4fY/ – nnnnnn Jun 22 '13 at 14:02
-1

I think this is interesting question for those how love JavaScirpt and I hope this is response what you are looking for:


var isContextSet = function (fn) {
   var testThis = {}; //unique object for testing what is this inside fn
   return fn.call(testThis) !== testThis;
}

I try to "set" this inside fn: fn.call(testThis); and if that function returned reference to newly created object (testThis) then it is not binded. I hope you get it :)

EDIT: This works when fn retuns this (as shown in question). Otherwise you can not define isContextSet properly.

Zango
  • 2,387
  • 3
  • 19
  • 33
  • What if the function does not `return this;` but an other value or no value at all? This will generated many false positives. Simplest example: `isContextSet(function() {})` will yield `true`, even though the function is not bound to a specific value. – Felix Kling Jun 22 '13 at 13:53
  • In question example function returns this. But if it doesn't then you are right. I guided from question. – Zango Jun 22 '13 at 13:57