The following code calls console.log
prints "hello":
console.log.call(console, "hello")
However, the code below throws TypeError:
x = console.log.call
x(console, "hello")
throws:
Uncaught TypeError: x is not a function
at <anonymous>:1:1
Can anyone explain this weird scenario?
(Of course it's the same for both call
and apply
)
The call() allows for a function/method belonging to one object to be assigned and called for a different object. call() provides a new value of this to the function/method. With call() , you can write a method once and then inherit it in another object, without having to rewrite the method for the new object.
The Difference Between call() and apply() The difference is: The call() method takes arguments separately. The apply() method takes arguments as an array. The apply() method is very handy if you want to use an array instead of an argument list.
Difference between call() and apply() method: The only difference is call() method takes the arguments separated by comma while apply() method takes the array of arguments. Example 1: This example uses call() method to call a function.
.call
gets the function to call from its this
parameter.
You're calling it via x
with no this
parameter, so it has no function to call (or rather, it tries to call window
) and gives an error.
You need to bind your x
variable to the log
function:
x = console.log.call.bind(console.log);
Bonus: .call
comes from Function.prototype
, and is the same no matter how you access it. Therefore, Function.call.bind(console.log)
also works (because Function
is a function and therefore has .call
). As does Date.call
.
Note: I will use apply
instead of call
in my answer just because the wording/reading is a bit less confusing but the same answer stands for call
.
You can imagine apply
looking something like this:
Function.prototype.apply = function apply(context, rest) {
// `this` in here is the function object on which we call `apply` as a method
// we then invoke whatever is bound to `this` (it should be the function that was "applied")
// and change its context and pass the rest of the arguments
// Note: I'm using `call` since we don't have access to native function code that can call a function with an overwritten context
this.call(context, ...rest)
}
When you call the apply
function (or any function for that matter) as a method on a function object (functions are first-class objects), this
within it gets bound to the function (object) on which you called apply
(this is one of the rules on how context is bound in JS when a function is called as a method of an object)
Function.prototype.apply = function apply(context, rest) {
this.call(context, ...rest)
// `this` is the function that `call` invokes
// in the example bellow, `this` is `console.log`
// so this function will do `console.log.call(console, 'example')`
}
console.log.apply(console, ['example'])
// ^^^^^^^^ console.log is the context because we are calling `apply` on it, with the dot (.) notation
However, when you store the apply
function into a variable and invoke it then, then the rule for this
binding is it to be undefined
(in strict mode or the global window
otherwise) and thus there's no function to call under the hood.
Function.prototype.apply = function apply(context, rest) {
this.call(context, ...rest)
// using the example bellow
// `this` is `undefined` now because `apply` was called like a regular function, not a method
// which means the code bellow does `undefined.call(console, 'example')`
}
// calling a function as a normal function (not a method via the dot notation), makes `this` be `undefined`
// therefore `apply` doesn't have a function which to call with an overwritten context
let apply = console.log.apply;
apply(console, ['example']) // error, `this.call is not a function`
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With