98

How does the Math.max.apply() work?.

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
</head>
<body>
  <script>
      var list = ["12","23","100","34","56",
                                    "9","233"];
      console.log(Math.max.apply(Math,list));    
  </script>
</body>
</html>

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max

The above code finds the Max number in the List. Can anyone tell me how does the below code work?. It seems it works if i pass null or Math.

console.log(Math.max.apply(Math,list));

Does all the user-defined/Native functions have call and apply method which we can use?.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Shane
  • 5,517
  • 15
  • 49
  • 79

5 Answers5

163

apply accepts an array and it applies the array as parameters to the actual function. So,

Math.max.apply(Math, list);

can be understood as,

Math.max("12", "23", "100", "34", "56", "9", "233");

So, apply is a convenient way to pass an array of data as parameters to a function. Remember

console.log(Math.max(list));   # NaN

will not work, because max doesn't accept an array as input.

There is another advantage, of using apply, you can choose your own context. The first parameter, you pass to apply of any function, will be the this inside that function. But, max doesn't depend on the current context. So, anything would work in-place of Math.

console.log(Math.max.apply(undefined, list));   # 233
console.log(Math.max.apply(null, list));        # 233
console.log(Math.max.apply(Math, list));        # 233

Since apply is actually defined in Function.prototype, any valid JavaScript function object, will have apply function, by default.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • How do say that max doesn't depend upon the current context?. – Shane Jan 21 '14 at 10:44
  • 3
    @Shane because, it works even when we set the context to `undefined` or `null` :) If `max` tried to access/change anything in the context, it would have failed when context is set to `undefined` or `null`. – thefourtheye Jan 21 '14 at 11:05
  • The first parameter is an illusion and doesn't make sense to me. How can `Math.max.apply(Math, list);` convert to `Math.max("12", "23", "100", "34", "56", "9", "233");` when doing `Math.max.apply(0, list);` or even something silly like `Math.max.apply(01231230210302302103923593, list);` is still equal to: `Math.max("12", "23", "100", "34", "56", "9", "233");` – NiCk Newman May 11 '15 at 13:56
  • 2
    @NiCkNewman Actually, `Math.max` will not use the first parameter, since it doesn't need any context. It operates only on the data which it receives in the arguments. – thefourtheye May 11 '15 at 15:30
  • Yeah, but what exactly is `apply()` passing in it's first argument? It's very confusing. Take this code for example: http://jsfiddle.net/agx85sba/4/ `GAMELENGTH4` should be returning 8, but it returns 2. It doesn't seem that `apply()`'s first parameter sends any data through and to me seems like an illusion. – NiCk Newman May 11 '15 at 16:08
  • 1
    @NiCkNewman Its too broad to discuss in the comments section. But this article on [`this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) would be a good starting point. It also has a section [`call` and `apply`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#call_and_apply) which details this. – thefourtheye May 11 '15 at 16:12
  • 2
    Best explanation of .apply() ever! – Microcipcip Sep 22 '16 at 14:05
  • If `Math.max` is called with no arguments it returnes negative Infinity so I would use `Math.max.apply(-Infinity, list);`. Also you risk exceeding the JavaScript engine's argument length limit with `apply()`. – A1rPun Oct 19 '16 at 10:16
  • @A1rPun I don't see any advantage in adding -Infinity as the first argument with an empty array, if you have more info on that, that would be great, here's an example showing null and -Infinity resulting in the same output: https://codepen.io/pythagoras1357/pen/BrBwwM – Claytronicon Mar 08 '18 at 00:10
  • 2
    Why null or math ? –  May 17 '20 at 19:49
46

On JavaScript ES6 just use the Spread operator:

var list = ["12","23","100","34","56","9","233"];
console.log(Math.max(...list));
//                   ^^^ Spread operator 
Community
  • 1
  • 1
  • 4
    To check the "Spread operator" compatibility: http://kangax.github.io/compat-table/es6/#test-spread_(...)_operator – pec Jan 09 '17 at 21:01
18

Can anyone tell me how does the below code work?

Math.max.apply(Math,list)

Invokes a Math.max function with Math object to be used as a this reference in the function implementation (body) and list to be passed as an arguments.

So this eventually equals to

Math.max("12","23","100","34","56", "9","233")

It seems it works if i pass null or Math.

Obviously Math.max implementation doesn't use instance variable - there is no reason to do so. The native implementation would just iterate over arguments and find the maximum one.

Does all the user-defined/Native functions have call and apply method which we can use?.

Yes, every single function can be invoked using call or apply

References:

C4d
  • 3,183
  • 4
  • 29
  • 50
zerkms
  • 249,484
  • 69
  • 436
  • 539
6

I will start by saying Math.max(...numbers) and Function.prototype.apply() should only be used for arrays with relatively few elements. (...) and apply will either fail or return the wrong result if the array is too large

Math.max.apply(null | undefined | Math, numbers) is the same as Math.max(...numbers) so I would recommend Math.max(...numbers) for aesthetic reasons.

const numbers = [5, 6, 2, 3, 7];

const max = Math.max(...numbers);

console.log('max:', max);
// expected output: 7

const min = Math.min(...numbers);

console.log('min:', min);
// expected output: 2

If you need to find the maximum element in a numeric array that is very large: use the Array.reduce() method.

Array.reduce() can be used to find the maximum element in a numeric array, by comparing each value:

const numbers = [5, 6, 2, 3, 7];
const getMax = (numbers) => numbers.reduce((a, b) => Math.max(a, b));

const getMin = (numbers) => numbers.reduce((a, b) => Math.min(a, b));
 
const max = getMax(numbers)
const min = getMin(numbers)

console.log('max:', max)
console.log('min:', min)

Conclusion:

Numeric array that is relatively small: use Math.max(...numbers) Numeric array that is very large: use the Array.reduce() method

voxun
  • 61
  • 1
  • 1
  • 2
    The JavaScriptCore engine has hard-coded argument limit of 65536, so when using apply() you should make sure that your array size will always be lesser. – Chaarmann Jun 18 '20 at 11:34
3

Math.max(val1, val2,...)

Math.max(1, 2, 3); // Math.max([value1[, value2[, ...]]])
value1, value2... are parameters and must be Numbers MDN Math.max

You can't pass array as Math.max parameters. To pass an array you have to use apply.

Apply

The first parameter of apply is this, the second is array. Math methods are equivalent of static in other languages, which means it doesn't need an object instance unlike to array.prototype.slice so you don't need to pass this in this case.

Example

var arr = [1, 2, 3];

Math.max(1, 2, 3);  // -> 3
Math.max(arr);      // -> NaN (Not a Number)
Math.max.apply(null, arr);  // Math.max.apply(null, [1, 2, 3]) -> Math.max(1, 2, 3) -> 3

arr.slice(1);  // -> returns Array [2, 3]
Array.prototype.slice.call(arr, 1); // Array.prototype.slice.call([1, 2, 3], 1) == [1, 2, 3].slice(1)

When you want to pass an array as parameter you have to use apply, otherwise use call.

apply = array
call = comma separated
More about call & apply

rflw
  • 1,747
  • 17
  • 26