.apply method question

How come in this code

function largestOfFour(arr) {
  return arr.map(Function.apply.bind(Math.max, null));
}

We use Function? What does Function represent/do?

Also Ive been doing some reading on apply and bind but I am still confused by this whole piece of code.

Also does the (Math.max, null) apply to both apply and bind as the given arguments? And why is null not the first argument?

Doesn’t apply need to have an array as an argument?

Thanks,

Andrej

You’re going to be better off reading this and asking questions afterwards:

1 Like

Function there refers to the Function Prototype which provides a direct reference to the apply method (since it only works on functions) but when you borrow methods like that you need to provide the context of this inside of the method’s execution. The first parameter in .bind, Math.max tells .apply what function or method it’s actually gonna apply some parameters on.

The rest parameters determine some extra arguments sent during the execution of this new function created by .bind (as specified in the documentation), and since the result of all this is a function, you can safely pass it to arr.map.

Here’s the interesting part; you now have a function that will call .apply to Math.max using null as its first parameter and receives subsequent arguments; which by the way, .apply only accepts two parameters anyway:

1- The context of this; since we don’t need it for Math.max.apply(), it can safely be anything. In this case, it just happens to be common practise to use null.
2- The array of arguments to send to the function being applied.

So now here’s how all of this unfolds: Let’s say during the first iteration of .map the newly generated function encounters this array: [5, -1, 9, 3]; clearly, 9 is the greatest number here.

What is actually happening inside is:

    +-- Function bound to
    |   the apply method.
    v
Math.max.apply(null, [5, -1, 9, 3]) <- Array of arguments needed
                 ^                     as the 2nd argument
                 |                     of the .apply method.
                 +-- The extra argument
                     specified in .bind.

It’s basically just functional programming and prototypes taken to an unnecessary extreme, you could do this instead:

return arr.map(subArr => Math.max.apply(null, subArr));
// or even with ES6:
return arr.map(subArr => Math.max(...subArr));

Seems shorter and concise.

1 Like

What does this mean?

i.e. Math.max.apply(null, [9, 43, 20, 6]); would invoke something like a Max.max method. What we’re looking for… almost.

?

Also why are we putting in null? You said it is just common practice?

OK that helped a bit but,

What does this mean?

i.e. Math.max.apply(null, [9, 43, 20, 6]); would invoke something like a Max.max method. What we’re looking for… almost.

?

Also why are we putting in null?

I understand your code it makes sense to me.

Im still trying to understand this code:

function largestOfFour(arr) {
  return arr.map(Function.apply.bind(Math.max, null));
}

Is the

(Math.max, null)

Parameters being passed to the apply or bind method? I thought the bind method only accepts one parameter which is the object for which “this” will get its value.

Thanks,
Andrej

Well, to answer your first question: Why do we put null there?

To understand why you have to first understand what this, .call, and .apply mean in general. Long story short, this is a reference to the execution context of a series of lines of code (function, method or global scope). Normally, the default value of this is the global context Window (in the browser) or Object [global] (in NodeJS):

image1

image2

But the execution context run inside a method that belongs to an object is the instance of that object prototype:

function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.sayHello = function() {
  return this.name + ' ' + this.age;
}

const me = new Person('Luis', 28);
me.sayHello();
//> "Luis 28"

Person.prototype.sayHello.apply({
  name: 'Someone Else',
  age: 100
});
//> "Someone Else 100"
// No array of arguments since
// sayHello() doesn't have params.

The execution context explanation by itself can fit inside a book chapter but since we’re working with objects here it’s ok to be concise. You can observe how the meaning of this was changed by apply. Same would have happened if I used me.sayHello.apply, me.sayHello.bind or Person.prototype.sayHello.bind since bind also changes the meaning of this and the added arguments are sent to the function it’s being bounded to a new context.

Then why sometimes we add null instead? Because sometimes a method doesn’t rely on this to yield a result. That’s exactly the case of Math.max; it works by comparing the arguments array-like object rather than this so we don’t actually need to set the context; but since the context is the first argument we can’t skip it so it’s common to put null or this.

Now, the second question: No, bind doesn’t only accept the new this context, it also accepts arguments separately which will be sent to the bound function. For example:

f.bind(newContext, arg1, arg2, arg3)();
// is now equal to
f(arg1, arg2, arg3); // with the new context of this

null is the first argument sent to .apply being called on Math.max and the resulting function will receive the second argument which is the array of parameters.