Calling a JavaScript Function With Arguments That Are Decided Later - javascript

I want to store a pointer to a function and then call it later with some arguments, but I'm having trouble with how to pass the arguments to the function.
For example:
var MyObject = (function () {
return {
myMethod: function (a, b) {
return a + b;
}
};
}());
var method = MyObject.myMethod;
var args = [2, 5];
method(args);
So in the last line, which I know doesn't do what I want method(args), I want to essentially call MyObject.myMethod(2, 5), so my array attempt fails and creating an object also fails. How do I pass in the stored arguments to the stored method?

you can use apply which accepts an array of arguments as an argument
var myarray = [2,5,...] //your array of arguments
method.apply(this,myarray); //execute your method
and in the method you have, you can receive the arguments via the "hidden" "pseudo-array" arguments argument.
function method(){
var args = arguments; //is [2,5,...]
}
this has advantage over call since you can pre-build the argument array and don't have to enumerate the arguments in the call's ()

K. Scott Allen has a great old article on this kind of thing. Use call or apply as a way to affect the value of this inside the called function.
http://odetocode.com/blogs/scott/archive/2007/07/05/function-apply-and-function-call-in-javascript.aspx

Related

Pulling parameters off a function passed as an argument

I am attempting to implement underscore's memoize function.
Memoize should be called as follows:
subtract = function(a, b) {
return a - b;
};
memoSub = _.memoize(subract);
memoSub(2, 1);
//1
Memoize should "remember" previous calls to its saved function, and rather than re-calculate (re-run the function), it should return those saved values.
I have the following structure set up, but the difficulty I'm having is understanding how I can access the passed-in function's parameters so that I can somehow save them, and thus check if they're the same as previously passed-in parameters.
Below is essentially an implementation of _.once, which I don't need to access the passed-in function's arguments for. I'm trying to modify it to make it into _.memoize
_.memoize = function(func) {
var called = false;
//i'd rather this be var storedArgs = {};
var result;
return function() {
//i'd rather be checking if these args have been seen before
if (!called) {
result = func.apply(this, arguments);
called = true;
//i'd rather save the computed results to the storedArg obj here
}
return result;
};
};
func.apply allows me to call a function and pass in the master arguments, i.e., everything after func in my function definition, but how then can I access the parameters/arguments of func? (In this case the parameters are (a, b) as seen above.
Edit: I suspect what I really need to do is somehow access the non-existent parameters here:
return function(/*how can I "capture" what will go here?*/) {...}
You already have your answer, the arguments object inside the inner function is exactly what you're trying to capture.
Both the inner and outer functions have an arguments variable but once you're in the inner function the outer variable is shadowed but the inner one.
You already have access to the arguments via arguments. What you need is a mapping of the arguments to the result. If the map contains an entry for the currently passed arguments, return the result. If not, compute it and store it.
Example:
var results = {};
return function() {
var key = Array.prototype.join.call(arguments);
if (!(key in results)) {
return results[key] = func.apply(this, arguments);
}
return results[key];
};
Note: This does a very simple serialization of the arguments and wouldn't work for complex arguments (objects).

Passing a function as an object property

This is what I would like to do:
function genTxnId(id) {
return id;
}
var ws;
ws = new funcB('A', {evaluator: genTxnId(25)});
But I find that once it goes into funcB, the "evaluator" property of the passed in object is evaluated before going into the function. Is there anyway to keep it as genTxnId(25) until it is used within funcB.
I know that doing this would keep it as a function:
var funcA = function(b) { return b;
Would keep it as a function but then I won't be able to pass in argument b.
Using .bind(), you can bind the parameters of the function to specific values without calling it. .bind() will return a function that you can then call elsewhere, passing in the remaining arguments that haven't been bound.
function getTxnId (id) {
return id;
}
function funcB (str, obj) {
obj.evaluator();
}
var ws = new funcB('A', {
evaluator: genTxnId.bind(this, 25)
});
The one caveat is that bind takes as its first parameter the object to which this will be bound. This means that you can't depend on the value of this in getTxnId to refer to the newly created object, which may seem more intuitive (albeit impossible, to my knowledge). But in general, you won't need to do that, so you can pass in any old object. You could even pass null if you won't ever use this inside that function, i.e. getTxnId.bind(null, 25).

Javascript call() function - extra parameters

I'm reading Javascript: The Definitive Guide 6th Edition. It teaches ECMAscript 5. Anyway, it doesn't explain certain things thoroughly, like the call() function for example. This is about the extent of the book's definition:
Any arguments to call() after the first invocation context argument are the values that are passed to the function that is invoked. For example, to pass two numbers to the function f() and invoke it as if it were a method of the object o, you could use code like this:
f.call(o, 1, 2);
In the next section the author builds a map function. I've been studying Ruby so I know how map works. My question is about the implementation using the call() function. It looks like this:
var map = function(a,f, o) {
var results = [];
for(var i = 0, len = a.length; i < len; i++) {
if (i in a)
results[i] = f.call(o || null, a[i], i, a);
}
return results;
};
It then defines a square function and puts map to use:
function square(x){
return x*x;
}
var array = [1,2,3,4,5];
var results = map(array, square);
What is the purpose of the i, and a parameters in the call() function? If I remove them I get the same results.
Array.prototype.map is defined to pass the index and the array to the callback, just in case you need them. For example, instead of square(x), you could use Math.pow(base, exponent):
var results = [1, 2, 3, 4, 5].map(Math.pow);
console.log(results); // [1, 2, 9, 64, 625]
This map behaves in the same way. You don’t have to use the arguments if you don’t need them in a particular case.
Function.call allows you to call a function as though it were a method attached to an object.
What this means is you can have a function that is defined somewhere unrelated to an object, and then you can call that function as though it was a part of that object. This is a long way of saying that when you use Function.call, you are telling the JS engine to use the first parameter whenever you use 'this' inside the function.
So:
function set_field_value(name, value) {
// do stuff
this[name] = value;
}
makes no sense by itself, because the special variable 'this' is not set to anything (meaningful)
But, if you use call, you can set it to whatever you want:
// if my_object = some object:
set_field_value.call(my_object, 'firstname', 'bob');
console.log(my_object.firstname); // prints 'bob'
The only important argument to call is the first one, (in the above case, my_object) because the first argument becomes 'this' inside the function. The rest of the arguments are passed 'as is' to the function.
So - in your example, the i and a arguments are there to make the map function look like other map functions, which provide the array (a) and index (i) that are being worked on.
Hope that helps,
Jay
PS - I strongly recommend the book 'Javascript: the good parts' - it makes a lot more sense than the definitive guide.
f.call in this example equals to square.call, and square requires only one parameter(x), so i and a are totally redundant here (and not used). Only a[i] is used by the function.
However, since you can pass in any function you want as the second parameter of the map function, chances are there will be another function instead of square coming up in the book, and that function would require those additional two parameters as well. Or you can make one example yourself to try it.
function threeParams(a, b, c) {
return [a, b, c]; // simply puts the three parameters in an array and returns it
}
var array = [1,2,3,4,5];
var results = map(array, threeParams);
Your main confusion is not really about the call method. It's more about how javascript treats function arguments.
Forget about call for a moment and let's look at a regular function to minimize the number of things under consideration.
In javascript, functions are allowed to be called with more arguments than is specified. This is not considered an error. The arguments may be accessed via the arguments object:
function foo (arg1) {
alert('second argument is: ' + arguments[1]);
}
foo('hello','world'); // this is not an error
Javascript also allows functions to be called with fewer arguments than specified. Again, this is not considered an error. The unpassed arguments are simply given the value undefined:
function foo (arg1,arg2, arg3) {
alert('third argument is: ' + arg3);
}
foo('hello'); // this is not an error
That's all there is to it. When the function passed to map() is defined to accept one argument but map() calls it with three the remaining two arguments are essentially ignored.

Why apply() here takes only one argument instead of two?

I'm reading the book Javascript: The Good Parts. And I'm confused by the following code.
Function.method('curry', function ( ) {
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function ( ) {
return that.apply(null, args.concat(slice.apply(arguments)));
};
});
Where is the null in slice.apply(arguments)?
arguments is being passed as the context (this), not the function's arguments.
It's equivalent to arguments.slice(), except that arguments.slice() doesn't exist.
That's the equivalent of calling slice() on an array with no arguments - i.e. it returns an array with all the elements of the original array. In this case, 'arguments' is not a true array, so calling Array.prototype.slice on it in effect turns it into one.
Two different functions are being invoked.
In the first case
var slice = Array.prototype.slice,
args = slice.apply(arguments),
for great explanation refer to
http://blog.sebarmeli.com/2010/11/12/understanding-array-prototype-slice-applyarguments/
apply method of Array.prototype.slice, is being invoked, which will convert the arguments passed to the function into an array.
In the second function apply method of function is being called. The usage and details of this function is well defined here
http://www.devguru.com/technologies/ecmascript/quickref/apply.html

How do you reference Array.prototype.slice.call()?

I am writing a script in which I need to clone arrays in many different places. For this reason, I would like to do the following to emulate a cloning function:
var clone = [].slice.call;
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = clone(arr1, 0);
Unfortunately, the above code results in: TypeError: object is not a function. I realize there are many functions out there to do deep cloning and shallow copies but I just want to use the built in method. Interestingly enough, the following does work:
var clone = [].slice;
var arr1 = [1,2,3,4,5,6,7,8,9,10];
var arr2 = clone.call(arr1, 0);
Does anyone know why the first block doesn't work while the second does? Is there any way to reference a functions call and apply functions without throwing errors when calling the referenced function?
I have to definitely agree with both Felix King and pimvdb. I think the only drawback to using the Function.protoytpe.bind() function is the fact that this is not a function that is available in all browsers (IE6 for example). An alternative would be to use a JavaScript library that provides the curry() function. Another alternative would be to define a function which gives you the ability to retrieve the call function for any other function. Here is a definition that I posted on my blog for such a function which I called getCall():
Function.prototype.getCall = function() {
var realFn = this;
return function(objThis) {
return realFn.apply(objThis, Array.prototype.slice.call(arguments, 1));
};
};
Now, with this definition, you could do the following to get a reference to the call function of the slice function:
var slice = [].slice.getCall();
You can clone an array by calling slice directly:
var arr2 = arr1.slice();
If you want a clone function, you can do:
var clone = function(arr) { return arr.slice(); };
If you really want to prototype function (which is not necessary as long as the function is not overwritten):
var clone = function(arr) { return [].slice.call(arr); };
Why can't you reference call or apply directly?
It does not work for the same reason assigning a method of an object to a variable does not "work".
If you call func.call() then this inside call will be a reference to func, a function object.
If you assign call to a variable then the context is lost. You have a reference to the generic call function. Thus you'd have to pass the correct context (the method you want to apply call to) as first parameter to call again:
var clone = [].slice.call;
var arr2 = clone.call([].slice, arr1);
This is not really an improvement and quite confusing.
call and apply are methods that every functions inherits from Function.prototype. Functions don't have their own version of them. [].slice.call === [].splice.call yields true.
The difference is the scope of the function, i.e. what "this" is. I'm not sure what the correct technical terms are, but the "this" is not the same when a function is called "stand alone" or as a property of an object.
var myObj = {};
myObj.foo = function () {
console.log(this.bar);
};
myObj.bar = 1234;
var fooRef = myObj.foo;
myObj.foo(); // 1234
fooRef(); // undefined
You can however create a function that wraps a call to the function and passes on all the arguments:
var fooEncapsulated = function () {
return myObj.foo.apply(myObj, arguments);
}
fooEncapsulated(); // 1234
For the record, the most common way of doing this is:
Array.prototype.slice.call(myArray, other, arguments, here);
The problem is that whatever_function.call is equal to Function.prototype.call. Thus, you effectively save a reference to Function.prototype.call and the information that it is the slice function is lost.
Compare it with a custom function:
Function.prototype.custom = function() { console.log(this) };
[].slice.custom(); // logs slice function
var ref = [].slice.custom;
ref(); // logs window object
A method of keeping the this value from being changed is using Function.prototype.bind:
var ref = [].slice.call.bind([].slice);
Now,
ref([1,2,3], 1); // [2, 3]
because when calling the .call function, the this value is bound to the slice function and everything works as expected.
Sweet and simple:
slice = Function.prototype.call.bind(Array.prototype.slice);
slice([1,2,3]); // [1,2,3]
slice({length:3,0:1,1:2,2:3}); // [1,2,3]

Categories

Resources