20

I know that inside the function it is this.

var func = function {
    return this.f === arguments.callee; 
    // => true, if bound to some object
    // => false, if is bound to null, because this.f === undefined
}

var f = func; // not bound to anything;

var obj = {};
obj1.f = func; // bound to obj1 if called as obj1.f(), but not bound if called as func()

var bound = f.bind(obj2) // bound to obj2 if called as obj2.f() or as bound()

Edited:

You can't actually call obj2.f() as f doesn't become a property of obj2

edit end.

The question is: how to find the object, that the function is bound to, outside of this function?

I want to achieve this:

function g(f) {
  if (typeof(f) !== 'function') throw 'error: f should be function';

  if (f.boundto() === obj)
    // this code will run if g(obj1.f) was called
    doSomething(f);

  // ....

  if (f.boundto() === obj2)
    // this code will run if g(obj2.f) or g(bound) was called
    doSomethingElse(f);
}

and partial application without changing the object that the function is bound to:

function partial(f) {
   return f.bind(f.boundto(), arguments.slice(1));
}

Consensus:

You can't do it. Takeaway: use bind and this with great care :)

esp
  • 7,314
  • 6
  • 49
  • 79
  • Unless the function is created using `.bind()`, functions aren't bound to anything. They can be referenced by objects, but there's no object binding. – the system Jan 13 '13 at 19:31
  • ...unless you're asking specifically about functions created using `.bind()`. I didn't notice your code comments at first. You may want to move that information to the question text. But even then I'm pretty sure the answer is no. – the system Jan 13 '13 at 19:35
  • First of all, `f.boundto()` should be saved to a variable to save the call the second time, and you should also use an `else if` instead of `if`, as if it *was* `obj1`, then it obviously isn't `obj2`. – h2ooooooo Jan 13 '13 at 19:51
  • Yes, I mean functions created by bind. Function f is just function definition. By the time it is called, it can be bound to something. Or function can be a method... Editing, anyway :) – esp Jan 13 '13 at 19:53
  • 2
    I strongly suspect the answer is that you cannot do that. – Pointy Jan 13 '13 at 19:54
  • Edited. By the way, bind not only creates a new function, it also makes it the property of the object passed to bind as its first parameter. – esp Jan 13 '13 at 20:08
  • 1
    @h2ooooooo boundto() is a hypothetical method, it doesn't exist. What's the point of making the code that others suggest as impossible more efficient? :) Just joking... – esp Jan 13 '13 at 20:13
  • 1
    Functions are independent entities. What `this` refers to is determined when they are called (i.e. at runtime). However, if `f` was already bound using `.bind`, then calling `.bind` again would not change `this` anyway. – Felix Kling Jan 13 '13 at 20:14
  • @Pointy I also can't find out how to do it, Flanagan who never failed me before doesn't seem to be interested how to do it... I just need it so badly... Thanks, anyway. – esp Jan 13 '13 at 20:15
  • 1
    See also http://stackoverflow.com/q/7282158/471129 for a discussion on the possibility of obtaining the original function from a bind result, which the other shoe of obtaining the `this`. (The answer was you can't, at least with the standard bind.) – Erik Eidt Jan 13 '13 at 20:16
  • Why not just pass the original object to `g()`? It's my understanding that it's pretty bad style to separate the function from its object... – Jeff Jan 13 '13 at 20:18
  • @FelixKling I agree. I can even say that the value of ```this``` can change while the function f is being run, if some other function that it calls binds arguments.caller to something else... But why can't I find out what it would be if I were to call function f right now and use ```this``` before doing anything else? – esp Jan 13 '13 at 20:21

4 Answers4

15

Partial Application

You can do partial application:

// This lets us call the slice method as a function
// on an array-like object.
var slice = Function.prototype.call.bind(Array.prototype.slice);

function partial(f/*, ...args */) {

    if (typeof f != 'function')
        throw new TypeError('Function expected');

    var args = slice(arguments, 1);

    return function(/* ...moreArgs */) {
        return f.apply(this, args.concat(slice(arguments)));
    };

}

What Object is this Function Bound To?

Additionally, there's a pretty straight-forward solution to the first part of your question. Not sure if this is an option for you, but you can pretty easily monkey-patch things in JS. Monkey-patching bind is totally possible.

var _bind = Function.prototype.apply.bind(Function.prototype.bind);
Object.defineProperty(Function.prototype, 'bind', {
    value: function(obj) {
        var boundFunction = _bind(this, arguments);
        boundFunction.boundObject = obj;
        return boundFunction;
    }
});

Just run that before any other scripts get run, and any script which uses bind, it will automatically add a boundObject property to the function:

function f() { }
var o = { };
var g = f.bind(o);
g.boundObject === o; // true

(Note: I'm assuming you're in an ES5 environment above due to the fact that you're using bind.)

Nathan Wall
  • 10,530
  • 4
  • 24
  • 47
  • Thanks for the answer, both parts are very useful (and I will use them:). I have a small question, if you don't mind. I understand how ```var slice = Function.prototype.call.bind(Array.prototype.slice);``` works (after a few minutes of thinking). But why can't you just use ```var slice = Array.prototype.slice.call;``` ? It will do the trick, won't it? Or am I wrong here?... – esp Jan 15 '13 at 00:30
  • Also, I would be very grateful if you could find time to answer [another question of mine](http://stackoverflow.com/questions/14329210/where-to-change-objects-prototypes-in-node-js). Thanks! – esp Jan 15 '13 at 00:32
  • 1
    `var slice = Array.prototype.slice.call;` is identical to `var slice = Function.prototype.call`... that is, it stores the `call` function, so it doesn't work. But, you do get the idea. We want to store a function that does `slice.call`. `call.bind(slice)` is the way to do that. Cheers :) – Nathan Wall Jan 15 '13 at 01:20
  • 1
    I see. So I can use ```Array.prototype.slice.call(arguments)``` but the moment I store this function to a variable it forgets who was supposed to ```call``` it. Same misunderstanding as with my original question, actually. Understanding that "function is a value" has more than one layer. JavaScript is quite far from being simple... A wolf in a sheep skin, indeed. – esp Jan 15 '13 at 01:28
  • 2
    As long as you're modifying .bind to capture the originally bound object, you could also capture the originally bound function.. Then you can support rebind as well, which addresses some related scenarios others have asked about. – Erik Eidt Jan 15 '13 at 18:13
  • Can you please also explain why ```args.concat(slice(arguments))``` needs slice? Why can't I use ```arguments``` there? I understand that _arguments_ is not a real array, but shouldn't _concat_ work with it anyway? Thank you! – esp Jan 19 '13 at 21:05
  • ... I actually tested without _slice_, it doesn't work. Does it mean that _concat_ arguments should always be true arrays? Doesn't concat just iterate it? – esp Jan 19 '13 at 21:23
  • `concat` does 2 different things depending on the arguments. If `concat` is given a true array, it iterates the array and adds each item in the array. However, if it is given a non-array, it adds the non-array as a whole. `[ 'foo' ].concat([ 1, 2 ], 'abc', [ 3 ], 4)` results in `[ 'foo', 1, 2, 'abc', 3, 4 ]`. The string `'abc'` is simply added to the array; it is not iterated. Array-like objects (like `arguments`) are not iterated either.. `arguments` will simply be added to the array as a whole. – Nathan Wall Jan 20 '13 at 02:04
7

A function in javascript is not technically bound to anything. It may be declared as a property of an object as in:

var obj = {
    fn: function() {}
}
obj.fn();

But, if you get the function into it's own variable by itself, it is not bound to any particular object.

For example, if you did this:

var obj = {
    fn: function() {}
}
var func = obj.fn;
func();

Then, when you call func() which in turn executes the fn() function, it will have no association with obj at all when it is called. Associating an object with a function is done by the caller of the function. It is not a property of the function.

If one were to use fn.bind(obj), that creates a new function that just internally executes a call to obj.fn(). It doesn't magically add any new capabilities to javascript. In fact, you can see a polyfill for .bind() to see how it works here on MDN.


If you are expecting this to always be a particular object no matter how a function is called, that is not how javascript works. Unless a function is actually a shim that forces an association with a hard-wird object when it's called (what .bind() does), then a function doesn't have a hard-wired association. The association is done by the caller based on how it calls the function. This is different than some other languages. For example, in C++, you can't call a function without having the right object to associate with it at call time. The language simply won't resolve the function call and you get a compile error.

If you are branching on types in javascript, then you are probably not using the object-oriented capabilities of the language properly or to your best advantage.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
3

Instead of binding the function func to the objects, why not try treating func as an object, that can hold obj1 and obj2 as its properties?

For example:

var func = function {
    this.object; // Could be obj1 or obj2
    return this.f === arguments.callee;
    // => true, if this.object is not null
}

var f = func;

f.object = obj1; // or func.object = obj2;

You can also write a function that handles whether or not the object is obj1 or obj2:

function g(f) {
  if (typeof(f) !== 'function') throw 'error: f should be function';

  if (f.object === obj)
    // this code will run if g(f) was called
    doSomething(f);
  if (f.object === obj2)
    // this code will run if g(f) or g(bound) was called
    doSomethingElse(f);
}

The reason is that you want to treat obj1 and obj2 as a property of the function f. However, when you bind, you are adding the function as a property of either obj1 or obj2. It's possible to bind the function to multiple objects, so it doesn't make sense to look for THE one object to which you bound the function; because you're adding the function as a subset of the object.

In this case, since you want to treat the object as a subset of the function, it might make sense to add a property into the function that can hold obj1 or obj2.

Stegrex
  • 4,004
  • 1
  • 17
  • 19
2

If you are the one doing the bind, you can add a field to the function to record the this for later testing.

var that = "hello";
var xyz = function () { console.log(this); }.bind(that);
xyz.that = that;

// xyz is callable as in xyz(), plus you can test xyz.that without calling
Erik Eidt
  • 23,049
  • 2
  • 29
  • 53
  • I understand, but no... This function f comes from another module and I don't know what that module was doing with it... :) It's getting funny, really... Just joking, sorry. – esp Jan 13 '13 at 20:24
  • right, so maybe you can replace the system bind to capture it for you (yuk!) ;) – Erik Eidt Jan 13 '13 at 20:26
  • seems to be the only option, but I am not sure it will affect modules that have different global namespace... – esp Jan 13 '13 at 21:02