how to use apply() in javascript [duplicate] - javascript

This question already has answers here:
Why should use "apply"?
(5 answers)
Closed 8 years ago.
when read the heatmap code, I find a code like
this._store.addData.apply(this._store, arguments);
I am very confused about this usage of 'apply'.Is there any different in the code below
this._store.addData(arguments);
thanks!

The point of using .apply here is that apparently the function arguments are in the array arguments. The equivalent code without .apply would be:
this._store.addData(arguments[0], arguments[1], ..)
Which is obviously troublesome if arguments is of unknown length.
Passing this._store again is only a necessity, it's not the point of this code.

The two are not the same.
.apply() as documented here on MDN, sets the value of this when the function is executed and passes an array of arguments to that function which will be expanded into normal function arguments.
Your second code example passes an array-like object of arguments to the actual function rather than an expanded list of arguments so they are not the same.
As an example, if there are three items in the arguments pseudo-array, then the first option will call your function like this:
this._store.addData(arg1, arg2, arg3);
And, your second code block will call it like this:
this._store.addData([arg1, arg2, arg3]);
Though technically, it's not even passing an array, it's passing a pseudo-array that doesn't actually have the array methods.
The reason for using .apply() is when you have a list of arguments in an array or array-like structure and you want to call a function using that list of arguments as the arguments to the function (passed to the function as normal arguments). It is very commonly used when you want to just forward the arguments from one function to the next or when you want to modify the arguments (e.g. remove one or change one) and then call another function with all the rest of the arguments.
Or sometimes .apply() is used only to control the this pointer and no arguments are passed (though .call() can also do that).
As a corollary, .call() is used when you know exactly how many arguments there are (e.g. you know there are two arguments AND you want to control the this pointer or .call() is often used when you just want to control the this pointer and don't have any arguments. An advantage of .apply() is that you don't have to know how many arguments there are - you can just pass on whatever arguments there were (that's why it's particularly useful when forward the args from one function to the next) like when calling a base class constructor or when proxying for a function like what .bind() does.
This this pointer will be the same in the two methods because calling obj.method() will set the value of this to obj which in both of your cases is this._store.
Note, that these two would be the same:
this._store.addData(arguments);
this._store.addData.call(this._store, arguments);

Related

javascript apply and call methods and chain together

In this article js log function, there is a statement:
Function.prototype.apply.call(console.log, console, arguments);
I'm really confused by this statement.
What does it do?
How can I analyse this statement?
Or with some thoughts or tools, I can figure it out step by step?
Can it be simplified to more statements to achieve the same result? for instance: var temp = Function.prototype.call(console.log, console, arguments); Function.prototype.apply(temp);
Thanks for the response.
Apply is a function on the function prototype. Call is also a function on the function prototype. Apply is a function, therefore, it has call on it's prototype. All this is doing is calling the apply function.
Read more about apply here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
What does it do?
It calls console.log with console as this during the call, passing along the arguments in the
pseudo-array arguments as discrete arguments to the function.
So if arguments had "hi", "there" in it, it would be the same as:
console.log("hi", "there");
How can I analyse this statement?
Or with some thoughts or tools, I can figure it out step by step?
Let's start with what the apply and call functions are: They each have the ability to call a function
using a specific this during the call and passing arguments to that function. apply gets those
arguments from a single array (or anything array-like). call gets those arguments as individual arguments.
Normally, you'd see apply or call used like this:
someFunction.apply(valueForThis, ["arg1", "arg2", "arg3"]);
// or
someFunction.call(valueForThis, "arg1", "arg2", "arg3");
The only difference between apply and call is how they expect to get their arguments (apply = in
an array-like thing, call = as individual arguments).
So why, then, isn't that code doing this?
console.log.apply(console, arguments);
It's being cautious: console.log is a function provided by the host. It may not be a true JavaScript
function, and so it may not have the apply property.
So that code is avoiding relying on console.log having the apply property.
Which is where Function.prototype comes in. It's a reference to the object that is the prototype of all true JavaScript functions.
That prototype is where the apply and call properties on JavaScript functions come from.
So if we're worried that console.log doesn't have it (e.g., in case it doesn't inherit from Function.prototype), we can grab apply from that prototype object directly.
So the code is using call to call apply, and using apply to call console.log.
Can it be simplified to more statements to achieve the same result?
Not really, there's not a lot we can separate. I'll try to use variable names to clarify:
var thisValue = console;
var functionToCall = console.log;
var applyFunction = Function.prototype.apply;
applyFunction.call(functionToCall, thisValue, arguments);
Let's take this one part at a time.
Function.prototype.apply, more commonly seen as myBigFatFunction.apply, lets you call a function with a different this context than it would normally have. The difference between apply and call is that the former takes an array of arguments, the latter takes direct arguments after the first. Example:
myStr.substring(5)
String.prototype.substring.apply(myStr, [5]);
String.prototype.substring.call(myStr, 5);
^ all equivalent
However, for reasons I can't fully explain myself, some browser-native functions accessible to JavaScript don't have this function as a property (eg, console.log.apply). It's still possible to manually call it in the same manner though; so that console is still this, when it calls log, and that's what the given function is doing.
The reason for all that complication is that they want to pass in the arguments special variable. This is a keyword that exists in all functions, which represents all arguments to the function as an array-like object (so, suitable for Function.prototype.apply)
Your variable suggestion would likely simply call console.log once with console as argument one, and arguments as variable two, and return the result, rather than give you a function in a variable. If you want a shortened reference, it's possible you could use Function.prototype.bind, but that could actually lengthen your code.

Why is knowing the difference between .call() and .apply() so important?

I've been reading a bunch of web pages that talk about JavaScript interview questions, and I noticed a great many of them say that it's important to know the difference between .call() and .apply().
For example:
http://www.sitepoint.com/5-typical-javascript-interview-exercises/
https://github.com/h5bp/Front-end-Developer-Interview-Questions
For the life of me, I cannot understand why this is considered so important. Does anyone know? To me, it's probably as important as knowing the difference between, say, a js long date and a js short date (that is, it doesn't seem very important to me). My gut instinct tells me someone influential at some point said it's critical to know the difference between .call() and .apply(), and everyone just copied what this person said. But maybe I misunderstand something about .call() and .apply()? I'm not sure.
Simply put, more advanced types of Javascript will occasionally need to use either .call() or .apply(), particularly code that is trying to proxy functions and pass on arguments or control the value of this.
If you're doing those types of things, then you have to know how both .call() and .apply() work so you can use the correct method (since they don't work identically). If you're not doing those types of advanced things, then a lot of Javascript can be written without using them.
As an interview question, I think it's a reasonable test on whether you understand some of the more advanced nuances of argument passing, calling methods and this handling in Javascript. If you want to do well on those types of questions, you will need to understand both .call() and .apply(), now how to explain what they do and how and when to use them and when you would use one vs. the other.
The whole concept of this and how it is controlled or set in Javascript is often not understood well by lesser experienced Javascript programmers (heck I've even seen some experienced developers that didn't understand it well) so quizzing a candidate on things related to that is a reasonable test and .call() and .apply() are somewhat central to that.
If you were developing library or framework code, you'd be even more likely to be using .call() or .apply() in the work you do.
Here's a basic summary:
Every function in Javascript is an object that has some properties. So, in addition to be able to call a function like func(), you can also reference the properties on it like func.length.
Two of the properties that every function has are .call() and .apply(). They both allow you to call the function in somewhat different ways than just calling it as func().
.call()
We all know, of course, that if you just want to call a function with a fixed number of arguments, that you can just execute func(arg1, arg2) and those arguments will be passed to the function, but what if you want to control what the this value will be when you pass those fixed arguments? Calling func as func(arg1, arg2) will cause the this value inside that function to be set to either to the global object which is window in a browser or if running in strict mode to undefined. Every function call in Javascript such as func(arg1, arg2) resets the this pointer in this way. This is where .call() comes in. You can execute:
func.call(someThisValue, arg1, arg2)
And it will do the same thing as func(arg1, arg2) except it will cause the this pointer to be set to someThisValue.
Note: you can also use .call() with just one argument and it will just set the this pointer and not pass any arguments.
func.call(someThisValue)
.apply()
But, what if your argument list is not fixed and your arguments are in a variable length array or array-like object. You can't really use .call() because you can't type out the right .call() statement for every possible list of arguments. This is where .apply() comes in. A canonical use for .apply() is when you're just trying to pass on the arguments from some other function call. This is common when you're proxying other function calls to slightly modify their behavior, but still call the original and the original either has various forms (so you don't exactly know the arguments that were passed) or lots of different types of function calls all go through this proxy. In this case, you can use .apply(), often with the arguments object. You can see an example of this in the MDN polyfill for .bind().
Let's say I want to hook some existing function named doIt() for logging purposes and doIt() has a number of different ways that it can be called. Using .apply(), I could do it like this:
// regular function already defined in your program
function doIt(arg1, arg2) {
// do something
}
// then actual usage elsewhere in the program would be just this:
doIt("foo", "bar");
// now install a proxy that can intercept all calls to doIt() and
// add some behavior before and after
(function(origDoIt) {
// replace doIt function with my own proxy
doIt = function() {
console.log("before doIt()");
// call the original function with all the arguments and this pointer
// that were passed
var retVal = origDoIt.apply(this, arguments);
console.log("after doIt()");
return retVal;
}
})(doIt);
FYI, .apply() can also be used to just set the this pointer as in:
func.apply(someThisValue)
In that particular case (and only that case), it works identically to .call().
Here's one of my favorite uses of .apply(). The Math.max() method accepts a variable number of arguments and it will return the largest number of any of those arguments. Thus:
Math.max(1,2,3,4)
will return 4.
But, using .apply(), we can find the max number in any array.
var list = [999,888,777,666,555,444,333,1000];
var m = Math.max.apply(Math, list);
console.log(m); // 1000
We're using .apply() to send the entire list array as the arguments to Math.max() so it will operate on the entire array.
Note, when ES6 is fully implemented everywhere or in your particular execution envirionment, you can also use the new spread operator to do something similar:
var list = [999,888,777,666,555,444,333,1000];
var m = Math.max(...list);
console.log(m); // 1000
which becomes kind of a shorthand for what we were doing with .apply()

Utterly bewildered by JS 'arguments' peculiarity [duplicate]

This question already has answers here:
Passing arguments forward to another javascript function
(5 answers)
Closed 7 years ago.
I have this call:
function del(Model, modelName, model_id, req, res, next, cb) {
if(req.query.optimisticDelete){
optimisticDelete(arguments);
}
else{
pessimisticDelete(arguments);
}
}
the problem of course, is that the arguments aren't passed correctly to the optimisticDelete and pessimisticDelete functions.
in an ideal JS world, this might work, but I can easily see why it doesn't.
But it doesn't take away from that fact that I just didn't want to type all the arguments out for each call, and in fact I wanted to omit the arguments in the del function signature also, so this would be the most ideal situation, although I need a reference to the req object, which I am now missing:
function del() {
if(req.query.optimisticDelete){
optimisticDelete(arguments);
}
else{
pessimisticDelete(arguments);
}
}
but of course, when arguments is passed, it does not seem to magically separate into separate arguments.
And also, this doesn't work either:
function del(Model, modelName, model_id, req, res, next, cb) {
if(req.query.optimisticDelete){
optimisticDelete(Array.prototype.slice.call(arguments));
}
else{
pessimisticDelete(Array.prototype.slice.call(arguments));
}
}
if you understand what I am trying to do, please let me know if it's possible and how,
You're calling optimisticDelete with a single argument, which is an arguments special object that contains all the arguments to the original call. If you want to spread them out, you need to use apply:
optimisticDelete.apply(null, arguments);
It's not necessary to convert arguments to an array first. MDN says:
You can also use arguments for the argsArray parameter. arguments is a local variable of a function. It can be used for all unspecified arguments of the called object. Thus, you do not have to know the arguments of the called object when you use the apply method. You can use arguments to pass all the arguments to the called object.
There are two problems. First, the arguments object is weird, but it's not that weird. Nothing magically turns into a parameter list across a simple function call. There is, however, Function.prototype.apply, which is ultimately what you want.
First however you'll want to turn arguments into a plain array:
var plainArgs = Array.prototype.slice.call(arguments);
Or, if you care about runtime efficiency:
var plainArgs = [];
for (var i = 0; i < arguments.length; ++i)
plainArgs[i] = arguments[i];
(Note — this step may not be necessary, but passing the arguments object out of a function tends to make optimizers throw up their little hands and give up on your functions.)
(Another note — totally ignore this stuff about passing arguments to .apply() being bad. I'm wrong.)
With that out of the way, you can use .apply():
optimisticDelete.apply(null, plainArgs);
The .apply() function expects its second argument to be an array. That array becomes the individual arguments to the function. The first argument to .apply() is the value you'd like this to take on in the function call; since you were calling optimisticDelete() "naked" in the original code, I passed null, but you can pass whatever you want.

Can someone explain this function in JavaScript [duplicate]

This question already has answers here:
Applying a Function to Null in Javascript
(5 answers)
Closed 7 years ago.
I am learning about call and apply in javaScript from a online TUT. This function allows more arguments to be passed, rather than having a fixed amount.
var calculate = function(){
var fn = Array.prototype.pop.apply(arguments);
return fn.apply(null, arguments);
};
What I am having difficulty wrapping my head around is this statement.
var fn = Array.prototype.pop.apply(arguments);
The presenter of the of the TUT, explained it as the following:
We are binding the apply method onto the arguments object. This is going to give us the Function Object and assign it to the fn variable. It will also remove the Function Object from the argumentsObject. Because the Array's pop method takes the final element in the array, it removes it from the Array and then assigns to what ever called the method. In this case the fn variable.
What confused me was the following:
We are binding the apply method onto the arguments object. This is
going to give us the Function Object
It will also remove the Function Object from the arguments
Object.
And when we write in the return statement:
return fn.apply(null, arguments);
Why are we including null?
Array.prototype.pop.apply(arguments);
When you have a function, there's automatically an arguments objects, which is an Array-like object of arguments. If you call this fake function:
someFunction('hello', 'world');
and someFunction looks like this:
function someFunction() {
console.log(arguments);
}
The console.log will output ['hello', 'world']. However, don't be confused... That is not an Array object! It is an "array-like" object. Therefore, you can't say arguments.pop()... because arguments doesn't have that method (it belongs to Array.prototype). However, normal Array objects do have access to Array.prototype (e.g. [1,2,3].pop() // => [1,2]).
When you say .apply(), the first argument is the context... It sets the this. So really, Array.prototype.pop.apply(arguments) is a clever way of mimicking arguments.pop(). But you can't do arguments.pop(), because it doesn't have a pop method.
In return fn.apply(null, arguments);, null is the first arguments because we don't need to set a new context for this example. arguments is the second arguments because it's being passed in to use with fn.
.apply() returns a function object, so it returns something like this:
function() { ... }
We can then later invoke that function.
By the way, .pop() mutates the original object (in this case, the array-like object arguments). So you're passing in arguments to fn, but it's missing the last item that was in it previously.
According to MDN:
Syntax
fun.apply(thisArg, [argsArray])
Parameters
thisArg:
The value of this provided for the call to fun. Note that this may not be the actual value seen by the method: if the method is a function in non-strict mode code, null and undefined will be replaced with the global object, and primitive values will be boxed.
argsArray:
An array-like object, specifying the arguments with which fun should be called, or null or undefined if no arguments should be provided to the function. Starting with ECMAScript 5 these arguments can be a generic array-like object instead of an array. See below for browser compatibility information.
The last argument passed to calculate is assumed to be a function. It is popped from the arguments list. (Using apply because arguments is not a real array.)
This popped function (fn) is called with the rest of the arguments list. (All other arguments passed to calculate). The arguments list no longer contains fn because pop() modifies the original object.
NULL is used because fn is called without a value for this. (See MDN)
If you call calculate for instance like
calculate(2, 3, function(a, b){ return a + b });
it will return 5.

Why is apply not already bound to functions in Javascript?

Assume, for the sake of this question, that I want to be able to create a function in Javascript that appends all of the elements of one array to another array. One way to achieve this, if you have access to the destination array, is to say:
var destination = [1,2,3];
var source = [4,5];
Array.prototype.push.apply(destination, source);
console.log(destination); // [1,2,3,4,5]
Now, since Array.prototype.push.apply is pretty ugly, I want to alias it to something nicer, like:
var pushAll = Array.prototype.push.apply;
Which I should be able to call with two arguments, the context (destination) and an array of arguments (source). However, when I try to use the alias, this happens:
pushAll(destination, [6,7]);
TypeError: Function.prototype.apply was called on [object global], which
is a object and not a function
So clearly the apply function is not bound to push, which led me to try this:
var pushAll = Function.prototype.apply.bind(Array.prototype.push);
pushAll(destination, [6,7]);
console.log(destination); // [1,2,3,4,5,6,7,8]
Which clearly works fine. My question is, why do I have to bind the push method to apply? Shouldn't Array.prototype.push.apply already be bound to apply? Why does calling it under a different name result in calling it on an unbound context?
why do I have to bind the push method to apply?
It's the other way round: You have to bind the apply method to the Array push function - you can bind it to other functions as well! Otherwise apply doesn't know which method to apply with the arguments.
Function.prototype.apply.bind(Array.prototype.push); does call the bind function on the apply function with push as the argument, the argument on which apply is then bound. The resulting function pushAll will, when called, invoke apply on the push function, and pass it's argument (the array and the arguments array) to it.
Shouldn't Array.prototype.push.apply already be bound to apply?
Nope. JavaScript is designed to bind the context at the call of a function, not already when it's being referred to as a property - there is no implicit binding on property access. Otherwise Array.prototype.push would already be bound to Array.prototype, before you could call any Function methods like bind/apply on it and try to use it with a different context.
Why does calling it under a different name result in calling it on an unbound context?
It's not so much different name, but different style. (Unbound) Functions do get their this value set to the object when they are called as a method on it, i.e. when the reference to the called function is a property access: destination.push().
This allows for great flexibility, you can "borrow" functions from an object and call them on other objects, still being the same (unbound) function. This is rather impossible in languages where function objects are no first-class objects.
If functions (even though they were meant to be methods) are called as plain functions (pushAll()), their this value will be undefined (unless in sloppy mode). Read more on the this keyword at MDN.

Categories