I am trying to call a function required number of times. To solve this, I am creating a function that takes rest parameters and iterating through the first argument. I am unable to unpack the rest arguments and pass it to the function. Is there a way that I could unpack and pass it to the function as parameters. Below is my working code. Is there a better way to make it work?
function hello(name) {
console.log("Hello "+ name);
}
function greet(name,time_of) {
console.log("Good " + time_of +" " +name);
}
function foo(times,x, ...args) {
for(i=0;i<times;i++) {
x(arguments)
//x(args); //works good for hello() but not for greet(). Doesn't pass them to second argument
x(args[0],args[1]); //This works but not scalable
//args.map((element) => x(element));
}
}
foo(2,hello,"Myname");
foo(3,greet,"Myname","Afternoon");
Just spread the args out again:
function hello(name) {
console.log("Hello " + name);
}
function greet(name, time_of) {
console.log("Good " + time_of + " " + name);
}
function foo(times, x, ...args) {
for (i = 0; i < times; i++) {
x(...args)
}
}
foo(2, hello, "Myname");
foo(3, greet, "Myname", "Afternoon");
Related
What is the best way to use parameters on JavaScript?
- use arguments variable
- define a parameter
function greet() {
console.log("Hello " + arguments[0]);
}
function greet(name) {
console.log("Hello " + name);
}
Well it depends on the usage. If you expect only to process one variable, then use the second one. If you don't know how many inputs you are processing, then you can use the first one.
// processing single input
function greetPerson(name) {
console.log("Hello " + name);
}
// processing multiple inputs that varies in number
function greetPeople(...people) {
if (people.length > 1) {
people.forEach(p => console.log("Hello " + p));
} else {
console.log("Hello " + people[0]);
}
}
Also, it's better to use the rest parameter so that your parameters that you passed in to the function would be an Array. The built in arguments object is not an array and does not let you Array methods such as forEach or map, which can be a pain if you need to use them.
I have a good handle on closures conceptually, but a problem has arisen that I can't quite understand.
When creating a function to pass some value to an inner function without binding it to the outermost function's final value when it returns, this doesn't appear to be allowed:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
function (x) { // this anonymous function definition...
var item = 'item' + list[x];
result.push(
return function () {
console.log(item + ' ' + list[x]);
};
);
}(i); // and simultaneous invocation...
}
return result;
}
Whereas if I move the closure completely inside the call to .push(), everything works out fine:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
result.push(
function (x) { // wrapper now inside the call to .push()
var item = 'item' + list[x];
return function () {
console.log(item + ' ' + list[x]);
};
}(i) // and called here...
);
}
return result;
}
What I'm wondering is: what rule am I violating when I define the anonymous function that wraps the closure immediately inside the for loop as opposed to inside the call to .push()?
By "not allowed", I'm assuming that the interpreter is complaining about a syntax error. What you have in the first case:
result.push(
return function () {
console.log(item + ' ' + list[x]);
};
);
Isn't syntactically valid.
But even if you remove the return:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
function (x) { // this anonymous function definition...
var item = 'item' + list[x];
result.push(
function () {
console.log(item + ' ' + list[x]);
}
);
}(i); // and simultaneous invocation...
}
}
You will still get an error. This is because you haven't specified the parentheses to the IIFE, which means function (x) { ... }() is treated as a declaration/statement regardless of the trailing (). But if you are declaring a function, you need to specify a name, which is why the ( after function is unexpected. If you want to treat it as an expression, you have to wrap it in (...) and hence use (function (x) { ... })().
In the second case, the argument to result.push(...) can only be an expression, so there is no ambiguity as to how function (x) { ... }() is to be interpreted; it can never be a declaration, and so it has to be an expression (either a function-literal or an IIFE).
As an analogy, think of function() { ... } like the string "hello". You can never use "hello" in isolation; the following code isn't syntactically valid:
var x = "foo";
"hello";
This is essentially what you are doing with the anonymous function:
var x = "foo";
function () {
}
What should be done with that function? It isn't not assigned to anything, just like "hello" in the earlier example isn't assigned to anything. But we know that functions can be invoked, so what we are doing with (function() { ... } ()) is saying "take this function I have defined here and then call it right now". It is analogous to calling a method on a string literal:
"abcd".substring(0, 2); // returns "ab"
And indeed, you could do something similar with a function, which I think demonstrates a little better what is happening with the IIFE:
// logs "hello"
(function() {
console.log("hello");
}).call();
The parentheses is a way to remove ambiguity and tell the interpreter that you want to treat the function as an expression/literal instead of a declaration. In the above example, if you removed the surrounding parentheses, you would get the same syntax error about the unexpected (.
There was a syntax error with the wrapper (IIFE) function and wrongly placed return statement in the first case. Here a fixed (and slightly modified) snippet.
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
(function (x) { // this anonymous function declaration...
var item = 'item' + list[x];
result.push(
function () {
console.log(item + ' ' + list[x]);
return item + ' ' + list[x];
}
);
})(i); // and simultaneous invocation...
}
return result;
}
buildList([1,2,3]).forEach(function(func) {
document.body.innerHTML += func() + '<br>';
});
In the first case, you are pushing return of your function declaration NOT invocation, which is effectively
result.push(return function(){...})
In the second case, you are pushing the return of IIFE function execution, which returns your original function, so its effectively
result.push(function(){...})
Obviously, second is what you want. You can change first to
result.push(
function () {
console.log(item + ' ' + list[x]);
};
);
to make it work. No return while pushing.
I've written a function which may take an unknown number of functions as parameters, but can't figure how I can make this work when one or more of the functions take parameters too.
Here is a quick example of what I would like to achieve:
function talk(name) {
console.log('My name is ' + name);
var callbacks = [].slice.call(arguments, 1);
callbacks.forEach(function(callback) {
callback();
});
}
function hello() {
console.log('Hello guys');
}
function weather(meteo) {
console.log('The weather is ' + meteo);
}
function goodbye() {
console.log('Goodbye');
}
// I would like to be able to do the following:
//talk('John', hello, weather('sunny'), goodbye);
You can pass an anonymous function which can call the function with required parameters
talk('John', hello, function(){
weather('sunny')
}, goodbye);
function talk(name) {
console.log('My name is ' + name);
var callbacks = [].slice.call(arguments, 1);
callbacks.forEach(function(callback) {
callback();
});
}
function hello() {
console.log('Hello guys');
}
function weather(meteo) {
console.log('The weather is ' + meteo);
}
function goodbye() {
console.log('Goodbye');
}
talk('John', hello, function() {
weather('sunny')
}, goodbye);
talk('John', hello, weather.bind(null, 'sunny'), goodbye);
In this case .bind is essentially returning a function with some bound parameters - quite analogous to Arun's answer, though you may consider this method a little more succinct / readable.
I am using a JavaScript API of some software. In software there are Solutions and they contain Worklists.
For each Solution I want to list Worklists.
solutions = obtainSolutionsformAPI();
for (i=0,i<solutions.length,i++){
sol=solutions[i];
sol.obtainWorklists(callbackFunction);
}
the callbackFunction is called by software and the array of worklists is passed as the argument.
function callbackFunction(arrayOfWorkLists){
for (j=0,j<arrayOfWorkLists.length,j++){
wl=arrayOfWorkLists[j];
console.log(wl);
}
}
now in console.log I would like to also print out the i variable (the number of solution) together witch each worklist, but how do I get the i inside callback function, when I am not the one who is calling it? I suspect I need to modify the callback function each time before I pass it to sol.obtainWorklists(callbackFunction); What is the correct way of doing this?
Edit: I do not want the function defintions to be nested (one inside other), because I have multiple nestings like this one and it would be quite unreadable. So I do not want this:
sol.obtainWorklists(function callbackFunction(arrayOfWorkLists){...});
You can use a builder function:
sol.obtainWorklists(buildCallback(i, callbackFunction));
...where buildCallback looks like this:
function buildCallback(index, func) {
return function() {
var args = [index];
args.push.apply(args, arguments);
func.apply(this, args);
};
}
Complete example below
Then in your callback, expect the i value as the first argument with the arguments that obtainWorklists normally provides following it.
How that works:
When you call buildCallback, you pass in the i value and the callback you want called.
buildCallback returns a new function which has that information bound to it (it's a closure over the index and func arguments in the call to buildCallback).
When obtainWorklists calls the function buildCallback created, we create an array with the i value (index) followed by the arguments that were received from obtainWorklists.
Then we call the callback, using the same this value that the function was called with, and the args array; your callback will see the entries in args as discrete arguments.
If you don't care about the this value in your callback, you can use ES5's Function#bind instead:
sol.obtainWorklists(callbackFunction.bind(null, i));
That does much the same thing, but doesn't preserve the this used to call it.
Complete example for builder function (live copy):
// obtainWorklists stand-in
function obtainWorklists(func) {
setTimeout(function() {
// Just call it with a random number
func(rand(100, 200));
}, rand(100, 500));
}
// Code to get worklists
var i;
for (i = 0; i < 10; ++i) {
obtainWorklists(buildCallback(i, callback));
}
function buildCallback(index, func) {
return function() {
var args = [index];
args.push.apply(args, arguments);
func.apply(this, args);
};
}
// Your callback
function callback(index, value) {
display("Index is " + index + ", value is " + value);
}
// ========= Utility functions
function rand(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
Complete example for Function#bind (live copy):
// obtainWorklists stand-in
function obtainWorklists(func) {
setTimeout(function() {
// Just call it with a random number
func(rand(100, 200));
}, rand(100, 500));
}
// Code to get worklists
var i;
for (i = 0; i < 10; ++i) {
obtainWorklists(callback.bind(null, i));
}
// Your callback
function callback(index, value) {
display("Index is " + index + ", value is " + value);
}
// ========= Utility functions
function rand(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
You need to use a closure:
solutions = obtainSolutionsformAPI();
for (i=0,i<solutions.length,i++){
(function(innerI) {
sol=solutions[innerI];
sol.obtainWorklists(function(worklists) {
callbackFunction(worklists, innerI);
}
})(i);
}
function callbackFunction(arrayOfWorkLists, i){
console.log(i);
for (j=0,j<arrayOfWorkLists.length,j++){
wl=arrayOfWorkLists[j];
console.log(wl);
}
}
A public method wants to call a private method (passing some objects as arguments) after some period of time... How do we do it?
Wanted something like this:
var someClass = function(someObj) {
var myPrivateMethod = function(someParam) {
alert('i got ' + someObj + '!');
if (someParam) alert('and i also got ' + someParam + '!');
}
var myDearPublicMethod = function(someParam) {
if (someParam) {
//call myPrivateMethod with setTimeOut so that it gets the someObj (and someParam if provided)
} else {
myPrivateMethod(someParam);
}
}
return {
somePublicMethod : myDearPublicMethod
}
}
someClass('something').somePublicMethod('somethingELSE');
Call an anonymous function that calls the function:
window.setTimeout(function(){
localFunction(someObject);
}, 1000);
it's not clear what is your goal
if (someParam) {
//call myPrivateMethod with setTimeOut so that it gets both someObj and someParam
} else {
myPrivateMethod(someParam);
}
why in the else branch you call a method with someParam as a parameter if in that branch you just verified that someParam is false, null or undefined? what the real difference between branches?