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.
Related
I have an array of functions, as in:
funcArray = [func1, func2, func3];
When in a given function, I want to execute the next function in the array. How do I do this? Here is my basic skeleton:
function func1() {
// I get current function caller
var currentFunc = func1.caller;
// I want to execute the next function. Happens to be func2 in the example.
}
I cannot use indexOf function, as one would for an array of strings or numbers.
NOTE: This question appears to be similar to this and the one it refers to. However, it is a different question.
I want to alter the sequence of processing by merely modifying the array. That's the goal. A possibly more efficient approach would be appreciated.
Clarification: Based upon some of the comments:
funcArray is global.
The goal is to implement middleware for a Node.js HTTP module in as simple and efficient a manner as possible without using any third-party modules.
Unless func1 closes over funcArray, you cannot have it reach out and find func2 and execute it, nor should you. Even if func1 does close over funcArray, it would be poor separation of concerns for func1 to reach out and find itself in funcArray and then execute func2.
Instead, have other code that's in charge of running the functions.
If they're synchronous
If the functions complete their work synchronously, then it's simply:
funcArray.forEach(fn => fn());
or
for (const fn of funcArray) {
fn();
}
or if the result of one function should be passed to the next, you can use reduce:
const finalResult = funcArray.reduce((previousResult, fn) => fn(previousResult), undefined);
...where undefined is the value to pass to func1.
If they're asynchronous
If they don't do their work synchronously, you'll need to provide them a way to notify their caller that they've completed their work. Promises are a good, standard way to do that, but you could use simple callbacks instead.
If you make them return promises, for instance, you can use the old promise reduce trick:
funcArray.reduce((p, fn) => {
return p.then(() => {
fn();
});
}, Promise.resolve());
or if the result of one function should be passed to the next:
funcArray.reduce((p, fn) => {
return p.then(fn);
}, Promise.resolve());
You can provide an argument to Promise.resolve to set the value to pass to func1 (without one, it'll receive undefined).
You can bind to the function the index where it is in the array so you can use this index to get and call the next function:
var funcArray = [func1, func2];
var boundFuncArray = funcArray.map((f, i) => f.bind(null, i));
boundFuncArray[0]();
function func1(nextFunctionIndex) {
console.log('func1 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
function func2(nextFunctionIndex) {
console.log('func2 called');
// Execute next function:
var nextFunc = boundFuncArray[nextFunctionIndex + 1];
nextFunc && nextFunc();
}
As T.J Crowder stated in the comment below, you can also bind the next function to the current one:
var funcArray = [func1, func2];
var boundFuncArray= funcArray.map((f, i, arr) => f.bind(null, arr[i + 1]));
boundFuncArray[0]();
function func1(nextFunc) {
console.log('func1 called');
// Execute next function:
nextFunc && nextFunc();
}
function func2(nextFunc ) {
console.log('func2 called');
// Execute next function:
nextFunc && nextFunc();
}
You can get the current function's name with arguments.callee.name, loop through the array of functions, and call the next function:
funcArray = [func1, func2, func3];
// Only func1() and func2() will be documented since the others have repeating code
function func1() {
// show the current function name
console.log(arguments.callee.name);
// loop the array of functions
for(var i = 0; i < funcArray.length; ++i)
{
// when the current array item is our current function name and
// another function exists after this then call it and break
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func2() {
console.log(arguments.callee.name);
// some logic which switches our next function to be func4()
funcArray[2] = func4;
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func3() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
function func4() {
console.log(arguments.callee.name);
for(var i = 0; i < funcArray.length; ++i)
{
if(funcArray[i] === arguments.callee && funcArray[i+1])
{
funcArray[i+1]();
break;
}
}
}
// call the first function
funcArray[0]();
Output:
func1
func2
func4
I have solved it this way:
// Adding next options to array
function addNext(array) {
array.last = 1
Object.defineProperty(array, 'next', {get:
function() {
if(this.last < this.length) {
this.last++
return this[this.last-1]
} else {
this.last = 1
return () => {}
}
}
});
}
// The functions for array (has to be function and not arrow function)
function first(param) {
console.log('first',param)
return this.next(param)
}
function second(param) {
console.log('second',param)
return this.next(param)
}
function third(param) {
console.log('third',param)
return this.next(param)
}
// The array
let fns = [first,second,third]
// Adding next option to array
addNext(fns)
// Run first function from array
fns[0]('test')
I dont know if your functions require certain parameters but this is the first thing that came to my mind.
var functArray = [
function() {
console.log("function1 executed");
},
function() {
console.log("function2 executed");
},
function() {
console.log("function3 executed");
},
function() {
console.log("function4 executed");
}];
functArray.forEach(function(x){
x();
});
The accepted answer and other comments did help me, but the way I implemented it is as follows:
//The functions are defined as variables.
//They do not get hoisted, so must be defined first.
func1 = function (arg1, arg2) {
//Code to do whatever...
...
//Execute the next function.
//The name of the function is returned by executing nextFunc()
global[nextFunc()](arg1, arg2, arg3);
}
func2 = function (arg1) { //Note different type of args
...
}
//Note that this is an array of strings representing function names.
funcArray = ["func1", "func2", "func3",...]
//Start the execution...
func1(arg1, arg2);
function nextFunc() {
var currentFuncName = nextFunc.caller.name;
var index = funcArray.indexOf(currentFuncName);
if (index < funcArray.length)
return funcArray[index+1];
}
The sequence of functions to be executed is easily managed through the array funcArray. The number or type of arguments is not fixed for each function. Additionally, the functions control if they should stop the chain or continue with the next function.
It is very simple to understand requiring basic Javascript skills. No overheads of using Promises.
"global" gets replaced by "window" for browser. This is a Node.js implementation. The use of function names in the array will, however, break if you minify the JS code. As I am going to use it on the server, I do not expect to minify it.
You can do it in this way with promise.all if your functions to be executed in parallel.
let toBeExecutedList = [];
toBeExecutedList.push(() => this.addTwoNumber(2, 3));
toBeExecutedList.push(()=>this.square(2));
And Then wherever you want to use them, do it like this:
const resultArr = await Promise.all([
toBeExecutedList.map(func => func()),
]);
'use strict';
let worker = {
someMethod() {
return 4;
},
slow(x) {
return x * this.somemethod();
}
};
function wrapper(func) {
return function(x) {
let result = this.func(x); // **
return result;
};
}
worker.slow = wrapper(worker.slow);
alert( worker.slow(2) );
When I run this code I get this error:
TypeError: this.func is not a function
If I replace the problematic line at ** with this:
let result = func.call(this, x);
I get the expected output. In this case it's 8.
I'm wondering why the way it's written is the wrong way to get "this" ie the object worker. In other words, how does the call function get the right object i.e. worker ?
The reason it's failing is that in the problematic line (highlighted with // **), you're effectively calling worker.func(x), which does not exist; You're not referring to the passed in variable at all.
If you add a function (func) to your worker, e.g.
let worker = {
someMethod: function() {
return 4;
},
slow: function(x) {
return x * this.somemethod();
},
func: function (x) {
return x * 10;
}
};
And call as above e.g.
worker.slow = wrapper(worker.slow);
console.log( worker.slow(2) );
You'd find it works, though this is not what you really want!
In JavaScript, any function call obj.f(ArgumentList) will be desugared into f.call(obj, ArgumentList...)(see ecma-262 7.3.12)
When wrapper(worker.slow) got a call, the function slow is passed into the wrapper and it produces another function which captured func argument. However, that function returned by wrapper is not called yet.
At a moment of the function call alert( worker.slow(2) )
func mean the argument that got captured in the closure and func.call(this, x) means it will call that function with the current caller, which is worker.
Therefore, `func.call(this,x)` will be `func.call(worker, x) `
which works because `func` exists in the closure.
while this means the reference to a current caller to the function(x){ ... }
Hence, `this.func(x)` will be `worker.func(x)`
which `func` does not exist in `worker`.
if you try to insert console.log(func) and console.log(this)
in that function, you will see the difference.
I've been trying to learn about closures, but one thing still perplexes me. If I have the following code:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
add();
add();
add();
// Returns "3"
If I call add() three times, why dosen't it set counter to zero every time, then return the anonymous funtion that increments counter by one? Does it skip over it once the self-invoking function runs? Sorry if the question seems simple, I'm having a hard time understanding it. Any help would be greatly appreciated.
If I call add() three times, why dosen't it set counter to zero every time, then return the anonymous funtion that increments counter by one?
Because add is that anonymous function, because the function containing counter got called and its result was assigned to add:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
})();
//^^----------- calls the outer function, returns the anonymous inner function
If you didn't call it:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
});
//^--- no () here
...then add would do what you said, it would return a new function with its own counter, each time you called it:
var add = (function () {
var counter = 0;
return function () {return counter += 1;}
});
var a = add();
var b = add();
var c = add();
console.log("a's first call: " + a());
console.log("a's second call: " + a());
console.log("a's third call: " + a());
console.log("b's first call: " + b());
console.log("b's second call: " + b());
console.log("b's third call: " + b());
console.log("a's fourth call: " + a());
console.log("b's fourth call: " + b());
.as-console-wrapper {
max-height: 100% !important;
}
That's not resetting counter, that's creating a new counter each time.
By calling add() you are not actually executing outer function but instead you are executing inner function. For inner functtion, counter is like a global variable that has been set once to 0 and then it was never set again to 0. On calling add() you are executing lines inside inner function thus increamenting counter.
The value assigned to add is the result of the IIFE, in which the closure was created. Maybe it's more obvious what will happen when add() is called, when its creating is written as follows (equivalent to your original code):
var add;
(function () {
var counter = 0;
add = function () {return counter += 1;};
})();
This question already has answers here:
JavaScript: Passing parameters to a callback function
(16 answers)
Closed 7 years ago.
I needed to pass a parameter to a callback function in Javascript, so I did the following which creates an anonymous function as a string and then passes it:
var f = "var r = function(result) {do_render(" + i + ",result.name,result.data);}"
eval(f)
$.getJSON("analysis?file=" + getParameterByName('file') + "&step=" + i,r);
This doesn't seem like a great idea however. Is there a better way?
There's several techniques that you can use to do this. One of which is to create a new function which "seals off" one of the variables:
function myCallback(i, result) { ... }
function createCurriedFunction(i, func, context) {
return function (result) { func.call(context, i, result); }
}
for (i = 0; i < 5; i += 1) {
var curriedFunc = createCurriedFuncion(i, myCallback, this);
$.getJSON(url, curriedFunc);
}
Context is the object for which the "this" will refer to in the callback function. This may or may not be needed for what you're doing; if not you can just pass in null.
There's actually a function that does exactly that called bind, and is used like
var curriedFunc = myCallback.bind(this, i), which will seal off the first variable.
It looks like you are having issues closing over i and to solve it used eval. Instead of that, simply close over it using an immediately invoked function expression (IIFE) like this:
(function(i){
//create a closure of the value of i
//so that it takes the immediate value of it instead of the end value
//based on the assumption i is from a loop iterator
$.getJSON("analysis?file=" + getParameterByName('file') + "&step=" + i,
function(result){
do_render(i, result.name, result.data);
}
);
})(i);//pass i into the IIFE in order to save its immediate value
You can simply do
var url = "myurl";
$.getJSON(url, function(result){
//callback function
});
Perhaps an odd question but here it goes: I have a function which I call periodically and within that function I need to know which iteration I'm in, or how many times the function has been called. A simplified version of the problem:
jQuery( document ).ready( function(){
setInterval( "myFunction()", 3000 );
});
function myFunction()
{
alert( "I have been called X times" );
}
So, how do I figure out the X in the above code?
Easy version: make a global variable like in codeling's answer. The problem - if some other code also defines a global variable with the same name, you're both in trouble.
Easy extended version - give the variable a crazy name that nobody will ever use: calledTimesED7E69A7B141457CA8908A612E3D7A3A
Clever version: append that variable to an existing global variable. Remember - everything's an object in Javascript!
$(function(){ setInterval(myFunction, 3000); });
function myFunction()
{
myFunction.calledTimes++;
alert( "I have been called " + myFunction.calledTimes + " times" );
}
myFunction.calledTimes = 0;
Traditional version: use scoping to hide that variable.
$(function()
{
var calledTimes = 0;
setInterval(function()
{
calledTimes++;
alert( "I have been called " + calledTimes + " times" );
}, 3000);
});
This hides "myFunction" though, so let's try again with a tricky kind of scoping:
var myFunction = null;
(function()
{
var calledTimes = 0;
myFunction = function()
{
calledTimes++;
alert( "I have been called " + calledTimes + " times" );
}
})();
$(function () { setInterval(myFunction, 3000); });
... and there are a zillion other ways you would hide that variable with scoping. Just pick your favorite.
You could simply use a global variable, which is increased each time you call the function:
var myFuncCalls = 0;
function myFunction()
{
myFuncCalls++;
alert( "I have been called " + myFuncCalls + " times" );
}
As soon as your code gets a little more complex (or if you use a lot of other libraries), you should, however, consider using scoping as shown in the other answers here (best explained in the one by Vilx).
Here's another interesting solution that doesn't use an external variable. The best part about this is you can leave any pre-existing functions untouched and call them as you would normally. That means if you're attempting to "tap in" to an function in an existing library, this will work very well for you. It adds an unobtrusive counter and allows you to continue calling existing functions normally; even with arguments!
// no js library required
// pre-existing function
var a = function(){
console.log("pre-existing function function");
console.log("arguments:", arguments);
};
// add counter func
var addFnCounter = function(target){
var swap = target;
var count = 0;
return function(){
swap.apply(null, arguments);
count++;
console.log("func has been called " + count + " times");
console.log("\n");
};
};
// usage
a = addFnCounter(a);
// call a() as you would normally
a();
a(1,2,3);
a('hello', 'world');
// using your setInterval example
setInterval(a, 3000);
Output
pre-existing function function
arguments: []
func has been called 1 times
pre-existing function function
arguments: [1, 2, 3]
func has been called 2 times
pre-existing function function
arguments: ["hello", "world"]
func has been called 3 times
setInterval output
pre-existing function function
arguments: []
func has been called 4 times
pre-existing function function
arguments: []
func has been called 5 times
pre-existing function function
arguments: []
func has been called 6 times
See it working here on jsfiddle
You'll have to use a closure.
Normally you would use a static variable. in Javascript it would look something like:
jQuery( document ).ready( function(){
setInterval( myFunction, 3000 );
});
var myFunction = (function(){
var count = 0;
return function(){
count++
alert( "I have been called " + count + " times");
}
})();
Demonstration: http://jsfiddle.net/MZQ83/2/
A static variable is cleaner and it won't pollute your outer scope either, compared to a closure or a decorator as in other answers.
var foo = function(){
alert( ++foo.count || (foo.count = 1) );
}
// test
function callTwice(f){ f(); f(); }
callTwice(foo) // will alert 1 then 2
or
callTwice( function bar(){
alert( ++bar.count || (bar.count = 1) );
}); // will alert 1 then 2
the second one is a named anonymous function. And note this syntax:
var foo = function bar(){ /* foo === bar in here */ }
Create a global variable and initialize by zero. then increment by one when myfunction() called. Display that variable instead of X.
ES6 / ES2015
You can use a Proxy for your function utilising the apply() trap:
const addCounter = fn => {
let count = 0; // keep count
//define handler
const handler = {
apply() {
//do something with this counter
console.log(`I have been called ${++count} times `);
return Reflect.apply(...arguments); //call the function normally
}
}
//wrap the function into a proxy that uses the handler and return it
return new Proxy(fn, handler);
}
setInterval( addCounter(myFunction), 1000 );
function myFunction() { //sample operation - move an image
const img = document.querySelector("img");
let offset = img.offsetLeft + 10;
if (offset > 100) //return to start
offset = 0;
img.style.left = `${offset}px`;
}
img {
position: absolute;
}
.as-console-wrapper {
max-height: 45px !important;
}
<img src="https://picsum.photos/150" />
You can use an Immediately Invoking Function Expression (or IIFE) to create a closure around the counter function. You can do it like this with ES6 Arrow functions:
const counterFunction = (() => {
let counter = 0;
return () => console.log(++counter);
})();
counterFunction();
counterFunction();
counterFunction();
Or with normal function expressions:
var counterFunction = (function() {
var counter = 0;
return function() {
console.log(++counter);
};
})();
counterFunction();
counterFunction();
counterFunction();
Read more about IIFEs
Read more about closures
There is an inbuilt function in JS called console.count()
My approach would add a property “count” to the function itself.
Just add one line at the beginning of your function you want to have tracked calls.
function func() {
func.count = (func.count || 0) + 1;
// first time you call the function func.count is undefined so use 0 instead
console.log("hi");
}
func();
console.log(func.count) // 1
func();
func();
func();
console.log(func.count) // 4
Functions are objects in javascript after all. No pollution of global namespace, no wrapping or closures needed, very simple to understand and to write.