Easier Way to Understand apply and call in JS
The first time I know apply
was when I met this code:
Math.max.apply(null, [1, 2, 3, 4])
As the mdn shows, the syntax is:
function.apply( thisArg , [argsArray] )Actually, in case above, thisArg
has no influence which means code below also works:
Math.max.apply(undefined, [1, 2, 3, 4]) Math.max.apply(Math, [1, 2, 3, 4])
The only effect of apply
in the code above is that it can pass the values in array to the function max
. So, code above equal
Math.max(1, 2, 3, 4)
Why would I mention this? Because we don't need this anymore because we already have ...
which works like:
Math.max(...[1, 2, 3, 4])
The reason that we still need apply
and call
is the thisArg
. They can help us call some powerful methods.
thisArg
in apply and call
I guess you might have seen this code:
Array.prototype.slice.call({ length: 2 }) function fn() { console.log(Array.prototype.slice.call(arguments)) } fn(1, 2, 3, 4) //[1,2,3,4]
Today, we don't need this either because of Array.from
. But I still want to talk about it for explanation. In the case above, call
was used because we want to do something like:
let obj = { length: 2 } obj.slice() //Uncaught TypeError: obj.slice is not a function
It would cause error because slice was defined in Array.prototype
. Only Array
instance can call that method. But actually in the implementation of slice
, it doesn't need to be called by Array
instance and there is a lot of methods like this. So, in this case, call
or apply
would let non Array
instance call these methods which means
Array.prototype.slice.call({ length: 2 }) //help you do let obj = { length: 2 } obj.slice = Array.prototype.slice obj.slice()
And to help it easier to understand , you can remember it like:
method.call(thisArg, ...args) //works like in most cases thisArg.method = method thisArg.method(...args) //for apply method.apply(thisArg, args) //works like in most cases thisArg.method = method thisArg.method(...args)
Wasn't that easy ?
So, let get back to Math.max.apply({}, [1, 2, 3, 4])
. You can remember it like:
let thisArg = {} thisArg.max = Math.max thisArg.max(...[1, 2, 3, 4])
And more cases:
Object.prototype.toString.call([]) //"[object Array]" //help you do this let thisArg = [] thisArg.toString = Object.prototype.toString thisArg.toString() //"[object Array]" //while [].toString()//""
Or
;[' sd ', 1, 3].map(Function.prototype.call, String.prototype.trim) //['sd','1','3'] //help you do ;[' sd ', 1, 3].map(function(...args) { return String.prototype.trim.call(...args) }) //help you do ;[' sd ', 1, 3].map(function(...args) { let thisArg = args[0] thisArg.trim = String.prototype.trim return thisArg.trim(...args.slice(1)) //Uncaught TypeError: thisArg.trim is not a function })
In the case above, it will got error because args[0]
is Primitive values
. You can't call methods in Primitive values
. But it can still help you understand.
More in apply
As apply
can accept an array-like object. So, what would happen if coding like:
Array.apply(null, { length: 2 })
Actually, it equals
Array.apply(null, [undefined, undefined])
So, you can understand it like:
let thisArg = {} //set null would get error in code below, also thisArg in above case is not important thisArg.Array = Array thisArg.Array(undefined, undefined)
Hope it's easier to understand apply
and call
.