View Single Post
Old 11-20-2012, 05:24 PM   PM User | #4
rnd me
Senior Coder

 
rnd me's Avatar
 
Join Date: Jun 2007
Location: Urbana
Posts: 3,453
Thanks: 9
Thanked 466 Times in 450 Posts
rnd me is a jewel in the roughrnd me is a jewel in the roughrnd me is a jewel in the rough
Quote:
Originally Posted by Dormilich View Post
@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?
__________________
my site (updated 5/13)
STATS (2013/5) HTML5:90.2% MOB:14% IE7:0.5% IE8:8.6% IE9:9.8% IE10:10%

Last edited by rnd me; 11-20-2012 at 05:30 PM..
rnd me is offline   Reply With Quote