When passing a callback function, can you set a parameter? - javascript

I am passing a callback function as a parameter to a function, and I am doing this in various parts of my web app.
I want the callback to react a little differently in some cases, can I pass a parameter somehow to this callback?
soemethod(callback);
otherethod(callback);
otherother(callback(a=1));
How could I pass a=1 in the callback?

Simply use an anonymous function wrapped around your parameterized function call:
otherother(function () {
callback(1); // assuming the first parameter is called a
});

No, you can't.
But you can do something like this:
soemethod(callback);
otherethod(callback);
otherother(callback, 1);
function otherother(callback, defaultValue) {
var value = defaultValue;
// your logic here, ie.
if(someCondition)
value = 2;
callback(value);
}

As others already mentioned, you can't pass default parameters like that in Javascript - you have to create the separate function yourself.
What you can do however, is use some very neat helper functions to create these closures automatically for you. One of the patterns I like the most is partial function application, where the "default" parameters are the leftmost parameters.
If you are using a new browser you can use Function.prototype.bind (it also handles the this parameter - this can allow passing methods as callbacks as well)
otherother( callback.bind(undefined, 1) );
//sets the first parameter to 1
//when the callback is called, the 2nd, 3rd, parameters are filled and so on
If you need to support older browsers as well, it is not hard to create your own partial application functions (lots of JS frameworks have one of some sort, the next example was taken from Prototype)
Function.prototype.curry = function() {
var fn = this, args = Array.prototype.slice.call(arguments);
return function() {
return fn.apply(this, args.concat(
Array.prototype.slice.call(arguments)));
};
};

Related

Executing a method inside a method

I'm currently working on a JavaScript exercise in FreeCodeCamp, and one of the test-cases my code should work with is a function call that looks like this:
addTogether(2)(3);
here is the bare-bones function I'm given:
function addTogether() {
return;
}
When I run the code below:
function addTogether() {
return arguments;
}
In the console, that the editor provides, I get:
TypeError: addTogether(...) is not a function
The instructions hint at using the arguments object, and it works well with test-case function calls that only have one argument object (i.e. addTogether(2, 3);), but not with the one I've shown above.
Is there a way to access/utilize the separate argument objects when they're in the format I showed above?
Note: I don't want any sort of answer to solve the problem, just info on any techniques on accessing the arguments of these type of function calls.
Help, is greatly appreciated.
Don't think of it as two separate sets of arguments. Think of it as you're calling another function (which you are). Functions are first-class values in JavaScript so you can use them just like any other value. This includes returning them from functions.
var f = function(y) {
console.log('f:', y);
};
var getF = function(x) {
console.log('getF:', x);
return f;
};
getF(1)(2);
Functions can also use values that exist in any parent scope. Loosely speaking, functions which do this are called closures.
function createGetX(x) {
return function() {
// Since x is in the parent scope, we can use it here
return x;
}
}
var one = createGetX(1);
console.log(one()); // Always
console.log(one()); // returns
console.log(one()); // one
Sometimes it helps me to think in a certain order. You can read the code "addTogether(2)" and stop before reading the "(3)".
From there you can see the exercise wants you to have that first part "addTogether(2)" return a function...since anytime there are "()" that means a function is getting called.
So "addTogether(2)" needs to return a function that takes one argument. Later that 3 will be one example of an input.
I think the name "addTogether" is a bit confusing..since that function's job is to make up and return the actual function that adds.
Sort of hard to explain this one without helping too much, but the bulk of your job here is to return a custom function, which includes the first variable (from it's scope) and expects another variable when it's called.
You could do this with closures.
function addTogether(x) {
return function(y) {
return x+y;
}
}

Are function-arguments not necessarily objects?

I'm learning functional programming and node.js, and I came across this odd problem when using Function.prototype.apply and .bind.
function Spy(target, method) {
var obj = {count: 0};
var original = target[method]
target[method] = function (){//no specified arguments
obj.count++
original.apply(this, arguments)//only arguments property passed
}
return obj;
}
module.exports = Spy
This code works, it successfully spies on target.method.
//same code here
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
//same code here
This code, however, does not. It gives an error message: TypeError: CreateListFromArrayLike called on non-object.
And then the biggest surprise is, this method works perfectly fine.
//same code here
target[method] = function (args){
obj.count++
original.bind(this, args)
}
//same code here
So why exactly do I get this error? Is it because function arguments are not necessarily objects? Or is it because apply has a stricter description than bind?
In this version:
target[method] = function (args){//args specified
obj.count++
original.apply(this, args)//and passed here
}
Here you are not taking all the arguments but just one, named args. Since apply expects an array like object you cannot use args since it is only the first argument passed to the original target.
You can change it to:
target[method] = function (arg){ //only one argument specified
obj.count++
original.apply(this,[arg]) //one argument passed here
}
Now it works, but you can only spy on one argument functions. Using call would be better since you only have one extra argument:
target[method] = function (arg){ //only one argument specified
obj.count++
original.call(this,arg) //one argument passed here
}
Now bind is a totally different animal. It partial applies functions, thus return functions. Imagine you need to send a callback that takes no arguments but calls a function with some arguments you have when making it. You see code like:
var self = this;
return function() {
self.method(a, b);
}
Well. bind does this for you:
return this.method.bind(this, a, b);
When calling either of these returned functions the same happens. The method method is called with the arguments a and b. So calling bind on a function returns a partial applied version of that function and does not call it like call or apply does.
bind is called the same way as call is, even though they do very different things.
If you really wanted to use bind in this way. You could use the spread operator (ES2015) to expand the arguments 'array' to individual arguments:
original.bind(null, ...args);
That will bind the original function with the array values as individual arguments.

Javascript function implementation variation

If i need to write a java script function that takes an argument and returns a function that returns that argument, i can think of following two implementations. Are both of these same ? or there is anything different technically ? Both works and returns the same result.
/*Implemenation 1*/
function myWieredFunc(arg){
var retf=function inner(){
return arg;
};
return retf;
}
/* Implemenation 2 */
function myWieredFunc(arg){
return function(){
return arg;
};
}
To use these:
var f = myWieredFunc(84);
alert(f());
This would be the way to write it
function wrap(arg) {
return function() {
return arg;
};
};
If you wanted to make it more flexible you could allow multiple arguments:
function wrap() {
var args = arguments;
return function() {
return args;
};
};
var later = wrap('hello', 'world');
var result = later();
console.log(result); // ["hello", "world"]
There is no reason for the variable in the code shown - functions are objects are values. As you've shown this means that function-objects can be assigned to a variable which is later evaluated and returned, or returned directly from the Function Expression.
As such, both forms are generally held equivalent and the closure over arg is unaffected.
However, in the first form..
Function.toString and stack-traces will normally include the function name, this makes "named functions", as in the first example sometimes more useful in debugging. Additionally, Firefox will expose function names - e.g. "inner" - through the non-standard Function.name property. (The function name can be specified without the use of the retf variable.)
Two additional bindings are introduced - retf in the outer function and inner in the inner function. These variables could be observed in the the applicable scope when stopping via a break-point - but are not otherwise accessible in the code shown.
They are the same thing, the second is using an "Anonymous" function which just means its a function that is not given a name or assigned to a variable.

Passing an array of arguments

I'm trying to have a function that is passed a function and calls it with a set of args. For example:
function foo(func, args) {
return func(args);
}
However, I don't know how many elements might be in args, and func should be able to be an arbitrary function that takes any number of args. How do I deal with this?
Also say I wanted to store the functions passed to foo and only call them if they hadn't been called before.
Can I just do something like:
var calledFuncs = [];
function foo(func, args) {
if(calledFuncs.indexOf(func) === -1) {
calledFuncs.push(func);
return func(args);
}
}
Thanks, I'm a bit new to functional programming in JavaScript.
You're looking for func.apply:
the first argument is the context aka this value. You don't want to change this, so pass it through.
the second argument is an array containing the arguments, which can of course be of dynamic length.
So you could do this:
return func.apply(this, args);
You seem to have a fine approach to your second issue (with the func(args) replaced). You may want to store the return value of called functions, though, since foo now returns undefined for any calls except the first.
Use apply:
func.apply(this, args);
apply takes an array of arguments as the second argument.
The first argument to apply will be the value of this in the scope of the function that you are calling. So you can pass in the current this or anything else that you want.
As far as your second question goes, that will only work for named functions. You can use func.name to store the function name in your array:
var calledFuncs = [];
function foo(func, args) {
if(calledFuncs.indexOf(func.name) === -1) {
calledFuncs.push(func.name);
return func(args);
}
}
This won't work for anonymous functions and it doesn't work for named functions in IE or Opera. You're going to have to parse it out, perhaps like so:
var name = func.toString().replace(/\n/g, "").replace(/\s*\(.*$/, "").replace(/^\s*function\s+/, "");
As far as your second question goes, you can do what you're doing right now. But I don't think it would work for the following case:
foo(function() {
}, []);
foo(function() {
}, []);
It will call both of those functions.
You want Function.prototype.apply:
func.apply(this,args);
Set the context (the first argument) to whatever you want, including null to get the window global (as you would get with your current func(...) invocation.
Although not directly related to your question, see also the related Function.prototype.call method that allows you to similarly set the context, but pass explicit parameters.

How to get all arguments of a callback function

Very often, I find myself using a callback function and I don't have its documentation handy, and it would be nice to see all of the arguments that are meant to be passed to that callback function.
// callback is a function that I don't know the args for...
// and lets say it was defined to be used like: callback(name, number, value)
something.doSomething( callback );
How can I determine what args its passing into that?
Note: looking at the source code can be unhelpful when the code itself is obfuscated and minified (as many js frameworks are)
To get the list of arguments without breaking functionality, overwrite the callback function in this way:
var original = callback;
callback = function() {
// Do something with arguments:
console.log(arguments);
return original.apply(this, arguments);
};
The context, this is preserved.
All arguments are correctly passed.
The return value is correctly passed.
NOTE: This method works in most cases. Though there are edge cases where this method will fail, including:
Read-only properties (e.g. defined using Object.defineProperty with writable:false)
Properties that are defined using getters/setters, when the getter/setter is not symmetric.
Host objects and plugin APIs: E.g. Flash and ActiveX.
Could it be as easy as
function callback() {
console.log(arguments);
}
?
Every function provides the arguments it has been called with in the automagic arguments collection.
Isn't this sort of the cart leading the horse?
Your function takes a callback. It's the method using your function that should be aware of what arguments the callback should accept.
You can even tell it which function's args to get using [functionName].arguments:
(function(arg1, arg2, agr3){
console.log('args are:', arguments);
return function fn(){
function m(){
console.log(
'fn.arguments:', fn.arguments,
'm.arguments:', m.arguments,
'argumentsX:', arguments
);
};
m('mArg1', 'mArg2', 'mArg3', 'mArg4');
};
})
(1, 2, Math.PI) // invoke closure
('fnArg1', 'fnArg2', 'fnArg3', 'fnArg4'); // invoke "fn"
Every function def rewrites the the arguments keyword to be of that scope btw (as seen with the "argumentsX" log).

Categories

Resources