Quote:
Originally Posted by Dormilich
@rnd_me: could you explain why var max=Math.max.apply.bind(Math.max, 0); requires a second parameter? (I tried various ones, it only matters that there is one)
.
|
a lot of folks, myself included, are confused at first by bind.
my big "aha" moment of understanding came by lining it up with the other two Function.prototype methods we all use and love; call() and apply().
turns out, bind() is EXACTLY like call() : it sets "this" with the first argument, and passes all other specified arguments as passed to the function you are calling it on. the only diff is that call() executes the function right there, whereas bind() stops short, returning the scoped function (the same as call would use) instead of the result.
let's throw bind() out, and examine the issue with the 1999 pieces.
you may have done something like this at some point:
Code:
Math.max.apply("something", [1,2,3,4])
you need to use "something" there for the same reason as zero (or something else) in my code.
apply's first argument is sucked into this.
if all that was passed was the array, max() would be called with the array as
this, and with no
arguments. apply() wants an array of arguments as
arguments[1] to turn into
arguments on the applied function.
the reason this works is because Math.max() doesn't look at
this, it only looks at it's
arguments, one number per argument:
Code:
Math.max.bind(0)([1,2,3]) //won't work
Math.max.call(0)([1,2,3]) //won't work
Math.max.call(0, [1,2,3]) // won't work
Math.max.call(0 ,1,2,3) // works
Math.max.apply(0, [1,2,3]) // works
Math.max.apply(999, [1,2,3]) // works
less obvious in my code is what's being bound.
it's not max(), it's apply().
check this out:
Code:
max=String.apply.bind(Math.max, 0);
max([1,2,3]) // ===3
yeah, that's weird, but it works because apply() doesn't care what is in front or behind it. it's a generic. i guess you can think of it as being "loose"; it doesn't care what function,
this, or
arguments it's hooked up with. heh.
the
apply() generic steps this out for greater detail:
Code:
apply=Function.prototype.apply; //the template for "all applies"
max=apply.bind(Math.max, 0); //bind apply to max
max([1,2,3]); //call apply, bound to max, on argument[0]
apply() wants two args: a new this and an array of other arguments.
bind wants 1+ arguments: this and others to pre-bake.
thus, we are essentially stacking the args for
apply onto the args for
max().
again, we can break it down to zoom in.
the
apply() method can be (jankily) written in userland using
call:
Code:
function apply2(that,a){
var args=JSON.stringify(a).trim().slice(1,-1);
var code= "this.call(that," + args + ")" ;
alert([
"APPLYING",
"arguments: "+[].slice.call(arguments),
"call code: "+code,
"this: "+ this,
"args: "+args,
"that: "+that
].join("\n"));
return eval( code );
}//end apply2()
//go ahead and make it work side-by-side with the "real" fn.apply():
Function.prototype.apply2=apply2;
//try it as a function method:
Math.max.apply2(0, [1,2,3]); //works
//try it as a generic:
apply2.call(Math.max, 0, [1,2,3]); //works
//try it as a bound method to turn arguments into an array:
apply2.bind(Math.max, 0)([1,2,3]) // works
hmm, that a bit of typing i just did, doh!
im tryin to stop my mic-hogging.
you can "alert-ify" the Function.prototype.bind replacement on the MDC page i linked to if you want to step-through
bind() like the
apply2 i made above.
does that help, or did i cloud the issue further?