Here are two callback function:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p)'
}
If I want use callback_a
function test(callback){
if(condition){
callback();
}
}
test(callback_a);
But the function test isn't applicable to callback_b, So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
There are three options:
The easiest way is to use spread operator:
function test(callback, ...callback_args) {
callback(...callback_args);
}
in this case the invocation of test for function callback_b would be like this:
test(callback_b,"b")
The second way is using arguments which are scoped to any function in JavaScript:
function test(callback) {
callback.apply(null, arguments.slice(1));
}
the invocation of test for function callback_b would be the same:
test(callback_b,"b")
Another options is to use partially applied functions. In this case you should define b_callback like this (ES6 syntax):
let callback_b = (p) => () => void{
alert('b says'+ p)'
}
or without ES6:
function callback_b(p) {
return function(){
alert('b says'+ p)'
}
}
and invoke it like this:
test(callback_b("b"))
There is a special object called arguments that gets created when a function is invoked. It's an array-like object that represents the arguments passed in to a function:
It can be used like this:
test();
// no arguments passed, but it still gets created:
// arguments.length = 0
// arguments >> []
test(a);
// ONE argument passed:
// arguments.length = 1
// arguments >> [a]
test(a,b,c,d);
// FOUR arguments passed:
// arguments.length = 4
// arguments >> [a,b,c,d]
Knowing this, one can call a callback with the rest of the arguments passed in from the parent function using apply like this:
function test(callback) {
callback.apply(null, Array.prototype.slice.call(arguments, 1));
}
// arguments passed into test are available in the function scope when
// .slice is used here to only pass the portion of the arguments
// array relevant to the callback (i.e. any arguments minus the
// first argument which is the callback itself.)
//
// N.B. The arguments object isn't an array but an array like object so
// .slice isn't available on it directly, hence .call was used here)
Might be worth reading up on:
The arguments object
Function.prototype.apply, Function.prototype.call and Function.prototype.bind as they are way to bind a context and arguments to a function (i.e. they'll work with the arguments object to call a function where you may not know how many arguments will be passed)
So how to implement a common function that you can passing some callbacks function with multiple possible parameter lists.
Basically, you don't. The function receiving the callback is in charge of what the callback receives as arguments. When you call Array#forEach, it's Array#forEach that decides what arguments your callback gets. Similarly, String#replace defines what it will call its callback with.
Your job is to say what test will do, what it will call its callback with. Then it's the job of the person using test to write their callback appropriately. For instance: You might document test as calling the callback with no arguments. If the caller wants to use callback_b, then it's up to them to handle the fact that callback_b expects a parameter. There are several ways they can do that:
The could wrap it in another function:
test(function() {
callback_b("appropriate value here");
});
...or use Function#bind
test(callback_b.bind(null, "appropriate value here"));
...but it's their problem, not yours.
Side note: If they pass you callback_b and you call it without any arguments, you won't get an error. JavaScript allows you to call a function with fewer arguments than it expects, or more. How the function handles that is up to the author of the function.
You can pass an anonymous function as the callback that will itself return your desired callback function with parameters.
test(function() { return callback_b(' how are you'); });
see this working snippet that will first use callback_a, then callback_b (with parameter) as the callback:
function callback_a(){
alert('a');
}
function callback_b(p){
alert('b says'+ p);
}
function test(callback){
if(true){
callback();
}
}
test(callback_a);
test(function() { return callback_b(' how are you'); });
You can pass the parameter while calling the callback
function test(callback){
if(condition){
callback();
}
else if(other condition){
callback("b");
}
}
test(callback_b);
You can write your callback function like
function callback_a_b(){
if(arguments.length){
var arg = [].slice.call(arguments);
alert('b says'+ arg[0])
}
else{
alert('a');
}
}
You can pass array of parameters as second param of test function or in ES6 use spread operator read more here
function test(callback, params){
if(condition){
if (params === undefined){
callback();
} else {
callback.apply(null, params); //params must be array
//ES6: callback(...params);
}
}
}
test(callback_a);
test(callback_b, [" whatever"]);
I've just checked in my browser (ffox 51.0.1) that the following works:
function test(callback,other_args){if(condition){callback(other_args);}}
results:
condition=true
test(callback_a)
=> shows the alert with 'a'
condition=false
test(callback_a)
=> doesn't show anything
condition=true
test(callback_b,"pepe")
=> shows the alert with 'b sayspepe'
condition=false
test(callback_b,"pepe")
=> doesn't show anything
Related
I'm trying to access arguments from the function that is being passed as a parameter to make sure to pass those through, however I'm getting the error:
TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
How can I get the arguments for func so that I can pass the message through when calling it?
UPDATE: maybe a better question is why can't I log func.arguments?
function a(func){
console.log(func.arguments)
return function(){
func.apply(null, func.arguments);
}
}
function log(message){
console.log(message)
}
a(log.bind('hello'))()
It can be done but its not pretty. you have to return an object with the parameters you want from the function you are passing
function a(func){
console.log(func.arguments)
//you dont actually need this since you have already called the function before passing
//return function(){
// func.apply(null, func.arguments);
//}
}
function log(message){
console.log(message);
return {
arguments : arguments //store the arguments and return them
};
}
//you not actually passing the function here (the way you have it written)
//you are calling it and returning its value
//we return { arguments: arguments } so the "a" function can retrieve its properties.
a(log("hello"));
I guess what you're trying to do is to intercept a function with a "logging" function.
If so, there is a different approach, using ES6 rest params --
let withLog = func => (...args) => {
console.log(...args);
return func(...args);
};
let add = (x, y) => x + y;
withLog(add)(2, 3); // outputs '2 3', returns 5
I am using the http module in node.js to read from url. The http.get function has this signature:
http.get(options[, callback])
The callback function takes a single argument, res. What can I do if I want to use extra objects/functions inside the callback? I can think of inlining the get call like so:
outerFunction = function(xyz) {
http.get(options, (res) => {
// do stuff with xyz here
xyz(res.blah);
}
});
But if my callback gets long I want to declare it upfront somewhere:
myCallback = function(xyz) {
return function(r) { xyz(r.blah); };
}
And invoke myCallback like so:
outerFunction = function(xyz) {
http.get(options, (res) => {
myCallback(xyz)(res);
});
}
But that seems super clumsy and only to get around the 1-arg callback restriction.
Are there better ways? Thanks!
you can use this code please,because myCallback return a function,then after get the resource,http will pass the res into xyz automatically.
outerFunction = function(xyz) {
http.get(options,myCallback(xyz));
}
You could use the arguments object.
The arguments object is a local variable available within all
functions. You can refer to a function's arguments within the function
by using the arguments object. This object contains an entry for each
argument passed to the function, the first entry's index starting at
0.
Quick example:
function test1234(a){
var args = arguments;
console.log(args); // prints -> 0: "a", 1: "b"
}
test1234('a', 'b');
Here is the behaviour I'm looking for:
function one(func){
func(5);
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(two(3)) //prints 3, 5
Can this behaviour or something similar be accomplished in javascript?
You can always use the bind() function to pass some arguments to your function. It'll create a new function with the first argument - arg1 - equal to the value of 3 in this example:
function one(func){
func(5);
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(two.bind(null, 3))
You can read more about the bind() function here: MDN - Bind
Some workaround is possible
function one() {
var args = Array.prototype.slice.call(arguments);
var func = args[0];
args.splice(0, 1);
args.push(5);
func.apply(this, args);
}
function two(arg1, arg2) {
console.log(arg1);
console.log(arg2);
}
one(two, 3)
There's a problem with your syntax: function one is expecting its single argument to be a function. Then, below, when you invoke it, you are not passing the function two, but whatever two returns when it's passed a single argument, probably undefined. I don't know what specifically you're trying to accomplish but I'd recommend a little research into closures.
function one(arg){
two(arg, 5); // func here is two so it requires two params...
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(3)// one expect function so can't execute function here!
as soon as one expects function as argument two(3) should return function.
this condition is required
so in order to achieve it your two function should be
function two(arg1){
console.log(arg1);
return function(arg2) {
console.log(arg2);
};
}
so two(3) function call gets passed as argument to one
so before assigning value to variable engine executes it. And execution of two(3) call logs 3 to console and returns function
function(arg2) {
console.log(arg2);
};
and then engine assigns executed value(returned function) to func variable.
so func parameter of one function now looks like
func = function(arg2) {
console.log(arg2);
};
one calls func with 5 passed in as argument.
so 5 gets logged to console.
Basically you can't specify the parameter in the function or it'll run. You need to specify the function aka one(two), but that obviously wouldn't work.
However if you dynamically create a function you should be able to accomplish the task like so :
function one(func){
func(5);
}
function two(arg1, arg2){
console.log(arg1);
console.log(arg2);
}
one(function(val) { two(3, val) }) //prints 3, 5
well somehow this things work for me
function one(func){
console.log("one is running");
func;
}
function two(args1, args2){
console.log("two is running");
console.log("args1 -> " + args1);
console.log("args2 -> " + args2);
}
//to call it
args1 = 6;
args2 = 12
one(two(args1,args2));
I am currently reading Eloquent Javascript Chapter 5. They give the following example which is confusing the hell out of me.
function greaterThan(n) {
return function(m) { return m > n; };
}
var greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));
// → true
Can anyone break this down to me as simply as possible. I have huge trouble with callbacks. Especially when it comes to situations like these.
Higher-Order functions basically mean two things:
Functions can take other functions as an argument/input
Functions can return functions
This is what is meant by higher-order functions.
// this function takes a function as an argument
function myFunc(anotherFunc) {
// executes and returns its result as the output which happens to be a function (myFunc)
return anotherFunc();
}
// let's call myFunc with an anonymous function
myFunc(function() {
// this returns a function as you see
return myFunc;
});
As for your example, it demonstrates higher-order functions by returning a function. It also demonstrates the notion of closure.
Closure is closing over a scoped variable, in this case the input argument n.
function greaterThan(n) {
// n is closed over (embedded into and accessible within) the function returned below
return function(m) { return m > n; };
}
// greatherThan10 reference points to the function returned by the greaterThan function
// with n set to 10
// Notice how greaterThan10 can reference the n variable and no-one else can
// this is a closure
var greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));
// → true
There's no "callback" involved here. What you've got with the "greaterThan" function is a function that returns another function.
So, you call the function:
var greaterThan10 = greaterThan(10);
Now the variable "greaterThan10" references the function returned by the "greaterThan" function invoked with 10 as the argument.
Then, you log the result of calling that function:
console.log(greaterThan10(11));
The function returned from "greaterThan" is invoked. It compares its parameter to the value of the parameter "n" passed when it was created. Because 11 is in fact greater than 10, the function will return true and that's what'll be logged.
I have a custom object that implements a function that'll be executed later. Here's how someone would call it:
customObject.onSomething(function(e) {
// do something with e
console.log('foobar');
});
Here's how onSomething is getting created:
var CustomObject = function() {
this.onSomething = function(callback) {
// If the user passes in parameter(s), how can I modify them before calling?
callback.apply(this);
}
}
How can I modify the argument(s) the user passed in before performing apply or call on the function?
apply takes a second parameter which is a list of arguments to pass to the function. call does the same, except it passes its own argument-list (everything after the first parameter which is used as this).
So, if you know which parameters you expect, you can just add them to the invoking function as the second parameter to apply (or as a list of parameters to call):
this.onSomething = function(arg1, arg2) {
// reverse the first and second arguments
callback.apply(this, [arg2, arg1]);
// equivalent:
callback.call(this, arg2, arg1);
};
If you don't know what kind of arguments to expect, but you still want to do something with them, you can do so with the builtin arguments pseudo-array which holds the arguments given to the current function (even when you don't declare them explicitly).
You can use this to invoke the callback with the same arguments given to the invoking function, or some transformation of them; e.g.:
this.onSomething = function() {
// call callback with the same arguments we got
callback.apply(this, arguments);
// or, make some changes
var newArgs = ["extra argument", arguments[1], arguments[0]];
callback.apply(this, newArgs);
};
Sounds like what you're asking for is fairly simple, see below:
var CustomObject = function() {
this.onSomething = function(callback, param1, param2) {
param1 += 4;
param2 = 'Something about ' + param2 + ' is different...';
callback.apply(this, [param1, param2]);
}
}