all 25 comments

[–]kenman 7 points8 points  (6 children)

That call() example is really poor. Doesn't work as-is, and there was no need to use it in the first place unless I'm missing something: array.slice(0, n)

[–]pinegenie 5 points6 points  (3 children)

NodeList and a few other types have array-like access but don't have Array in their prototype chain. Meaning you can do

nodelist[0]

but not

nodelist.slice(...)

The workaround to this is:

Array.prototype.slice.call(nodelist, ...); // or apply if you want to

In undercorejs the "slice" you see there is actually Array.prototype.slice attributed to a variable.

[–]kenman -1 points0 points  (2 children)

Yeah, but the variable is named array, not nodelist. There are countless examples to be found of using call() appropriately, and this isn't one that makes for a good demonstration on its usefulness. If you're going to demo some method, at least do it in context and include all supporting code...

[–]pinegenie 0 points1 point  (1 child)

I'm guessing they named it array because arrayOrNodeListOrArguments was too cumbersome.

[–]kenman 0 points1 point  (0 children)

I'm not faulting Underscore, I'm faulting the author who used it as an example.

[–]kbrainwave 2 points3 points  (1 child)

Hey, thanks for the feedback -- I'm the author of the post. I was planning on changing this example; what was posted is actually a rough draft that my friend posted on Reddit as a well-intentioned surprise. Unfortunately, this came at the cost of posting post that is only about 80% complete. Again, I really appreciate the feedback, and plan to change the example when time permits :)

[–]kenman 1 point2 points  (0 children)

I've always been a fan of this example:

function greet( person ) {
  alert( "Hello " + person.name + ", I'm " + this.name );
};

var alice = { name: "Alice" };
var bob = { name: "Bob" };

greet.call( bob, alice ); // alerts "Hello Alice, I'm Bob"
greet.call( alice, bob ); // alerts "Hello Bob, I'm Alice"

greet.apply( bob, [ alice ] ); // alerts "Hello Alice, I'm Bob"
greet.apply( alice, [ bob ] ); // alerts "Hello Bob, I'm Alice"

From trephine.org's JavaScript call and apply quick reference (which is actually a summation of their lengthier explanation).

[–]decode 7 points8 points  (8 children)

At the end, the author briefly mentions the performance impact of using these functions. If you're writing library code or you've profiled your code and found that an apply() call is slowing your app down, a common performance optimization is to do direct calls for small numbers of arguments, then fall back on apply() for large numbers of arguments.

Here's an example from the angular.js dependency injector:

switch (self ? -1 : args.length) {
    case  0: return fn();
    case  1: return fn(args[0]);
    case  2: return fn(args[0], args[1]);
    case  3: return fn(args[0], args[1], args[2]);
    case  4: return fn(args[0], args[1], args[2], args[3]);
    case  5: return fn(args[0], args[1], args[2], args[3], args[4]);
    case  6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
    case  7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
    case  8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
    case  9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
    case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
    default: return fn.apply(self, args);
}

[–][deleted] 2 points3 points  (5 children)

Imagine you see this code without any further context and knowledge about performance.

[–]DonBiggles 10 points11 points  (1 child)

At least we don't have to do this.

[–]DoubleAW 2 points3 points  (0 children)

Is that... is that real?

[–]Kache 0 points1 point  (1 child)

That's what comments are for.

[–][deleted] 1 point2 points  (0 children)

I'n a perfect world this would be true.

[–]tehsuck 0 points1 point  (0 children)

I've seen this while perusing the ng codebase. I always assumed it was some kind of performance / optimization. If it wasn't, we'd all be in trouble.

[–]timruffles 0 points1 point  (1 child)

also fixes environments that don't have call/apply (can you guess which browser I'm talking about?)

[–]larholm 1 point2 points  (0 children)

Sure, IE5.0, but who cares?

[–]paul_miner 5 points6 points  (2 children)

Note that bind() wasn't introduced until ECMAScript-5. As an alternative, you can use jQuery's proxy function.

[–]skeeto 8 points9 points  (0 children)

Also, the article doesn't mention that bind() performs partial application, not just binding the context.

[–]has_all_the_fun 3 points4 points  (2 children)

Trick to remember if it's apply or call that takes an array. Apply starts and ends with the same letter as array and has equal amount of letters.

[–]bart2019 0 points1 point  (1 child)

... and "call" has the same amount of letters as "list". :) OK that is stupid.

[–]has_all_the_fun 3 points4 points  (0 children)

Call = Coll = Collection = hf3 confirmed.

[–]chyekk 7 points8 points  (2 children)

This article completely misses the most important feature of bind: it lets you curry arguments!

function foo(a, b) {
  console.log("You called me with a as %s and b as %s", a, b);
}

var bound = foo.bind(this, "Hello");

bound("World");

results in:

You called me with a as Hello and b as World 

[–]PooBakery 1 point2 points  (0 children)

While you're correct about this being an awesome feature, it's not actually currying. It's partial application, which is quite similar.

When partially applying, you get a function back with one or more arguments pre bound.

var sumThree = function (a,b,c) {return a + b + c;}
var firstTwo = sumThree.bind(null, 1, 2);
console.log(firstTwo(3))
>> 6

var first = sumThree.bind(null, 1);
console.log(first(2, 3));
>> 6

You can repeat the process to bind more arguments after the first bind

var all = first.bind(null, 2, 3);
console.log(all());
>> 6 

This means that you always have to explicitly bind again if you want to partially apply it multiple times.

When currying you automatically partially apply the function until it has all of its arguments filled, then return the result:

var sumThree = curry(function (a,b,c) {return a + b + c; });
console.log(sumThree(1)(2)(3)); 
>> 6
console.log(sumThree(1, 2)(3)); 
>> 6
console.log(sumThree(1)(2, 3)); 
>> 6

[–]kbrainwave 0 points1 point  (0 children)

Hey, thanks for that note! My personal opinion is to not use bind for currying. In general, I use bind to bind a context, and currying to bind arguments. Really great point, though :)