5

I've a function myfunc and want to bind it to a specific this argument and other arguments to bind as a single array, not parameters list (cause I get the parameters list as an argument of function, where this code is executed). For that purpose I use apply on bind as follows:

var myfunc = function(arg1, arg2){
    alert("this = " + this + ", arg1 = " + arg1 + ", arg2 = " + arg2);
}
var bindedMyfunc = myfunc.bind.apply("mythis", ["param1", "param2"]);
bindedMufunc();

This results in Uncaught TypeError: Bind must be called on a function.

What am I doing wrong? Could you explain in detail, what's going onwhen I run this code, cause reality seems to contradict my opinion on that?

Summary of answers: Seems that bind itself has its own this argument, which is the function, it is called on. E.g. when you say myfunc.bind(args), bind's this is myfunc.

By calling apply on bind I've mistakingly assigned bind's this to "mythis", which is not a function and bind can't be called on it.

So, the solution is to use

myfunc.bind.apply(myfunc, ["mythis"].concat(["param1", "param2"]))

Also, if you want to call the binded myfunc right off, you could say:

myfunc.apply.bind(myfunc)("mythis", ["param1", "param2"])

but this doesn't suffice my case, cause I need to pass the binded function as an argument to addEventListener.

Thanks for your help, guys!

Boris Burkov
  • 13,420
  • 17
  • 74
  • 109
  • The first solution is `myfunc.bind.apply(myfunc, ["mythis", "param1", "param2"]))`, you forgot the array literal in your summary. – Bergi Apr 19 '15 at 23:37

3 Answers3

7

You should use the function as the first parameter to the apply method. The use of myfunc.bind doesn't associate the function with the call, it has the effect of Function.prototype.bind, and you can just as well use that.

The first parameter for the bind method (thisArg) should be the first item in the array.

var bindedMyfunc = Function.prototype.bind.apply(myfunc, ["mythis", "param1", "param2"]);
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 1
    Ah, I thinks I understand, what was wrong thanks to your example. `bind` has its own `this`, which should be the function it is called on (`myfunc`) in this case. So, I have to give `myfunc` as first argument and then build a list of arguments from `mythis` and `[param1, param2]` with `concat`. – Boris Burkov Apr 19 '15 at 22:32
3

Seems that bind itself has its own this argument, which is the function, it is called on. E.g. when you say myfunc.bind(args), bind's this is myfunc.

Exactly. If you want to apply bind, then you have to apply it to the function (the first parameter), and pass the bind arguments (including the intended this value) as an array (the second paramter):

(Function.prototype.bind).apply(myfunc, ["mythis", "param1", "param2"])
// which is equivalent to
myfunc.bind("mythis", "param1", "param2")
(…args) => myfunc.call("mythis", "param1", "param2", …args) // ES6 syntax

However, there is another way to solve your problem: bind apply to the function, and partially apply the proposed apply arguments:

(Function.prototype.apply).bind(myfunc, "mythis", ["param1", "param2"])
// which is equivalent to
(…args) => myfunc.apply("mythis", ["param1", "param2"], …args) // ES6 syntax
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The problem with the second method is `..args` becomes useless, I was also thinking about something like this `(fn.call).bind.apply(call, [fn, "this", params]).apply.bind()` but following all of the `this` values becomes so convoluted it'd be cleaner to write a wrapper – Paul S. Apr 20 '15 at 13:29
  • Yes, `…args` become useless, but it seemed you didn't want to use them anyway in your `bindedMufunc();` call? If you want `bindedMufunc` to receive further arguments, you have to use the first approach. – Bergi Apr 20 '15 at 13:31
  • 2
    @PaulS. Sorry, guys, for introducing some confusion yesterday with edits of my question - just successfully tested out`myfunc.bind.apply(myfunc, ["mythis"].concat(["param1", "param2"]))` solution. It works ok. Thanks for your help! – Boris Burkov Apr 20 '15 at 19:34
  • @Bob: I think you should accept Guffa's answer then – Bergi Apr 20 '15 at 20:34
2

Maybe you want to bind apply rather than applying bind?

var bindedMyfunc = Function.prototype.apply.bind(myfunc);
bindedMyfunc('obj', [1, 2]); // this = obj, arg1 = 1, arg2 = 2

I often use this pattern to make hasOwnProperty checks shorter without being shadowable;

var has = Function.prototype.call.bind(Object.hasOwnProperty);
has({foo:1}, 'foo'); // true
has({foo:1}, 'bar'); // false
Paul S.
  • 64,864
  • 9
  • 122
  • 138