This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I am seeking help understanding why the way I am using anonymous functions are erroring in some circumstances.
In the snippet below I popuplate 2 arrays with functions which are invoked at a later stage.
var arr1 = [];
var arr2 = [];
// callback function
var func = function(i){
return function(){$("body").append(i);}
};
var i = 1;
while(i <= 5)
{
arr1.push(function(){$("body").append(i);});
arr2.push(func(i));
i++;
}
// invoke functions
$("body").append("Output from arr1 == ");
for(var c = 0; c < arr1.length; c++){ arr1[c](); }
$("body").append("<br> Output from arr2 == ");
for(var c = 0; c < arr1.length; c++){ arr2[c](); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Now, I think I understand why arr1 outputs 66666, because at the time of invocation, i == 6 and becuase it has not been stored as a param within that function at time of creation, i retains it's last known value?
What I really don't understand is why i recieve TypeError: arr2[c] is not a function When I change the callback function to:
var func = function(i){
$("body").append(i);
};
Why does this happen and is this the most appropriate / elegant way to achieve this functionality.
For the first part of the question, you are right, it will retain the latest known value. To avoid that, you need to use closure. For example:
(function(x) {
arr2.push(func(x));
})(i);
Closure can often be used as a "scope hack". In this case, it will only ensure that the injected value is the constant current value of i.
For the second part of the question, your confusion is probably caused by the fact that you insert the result of the function (i.e.: func(i)) instead of the function itself (func).
In the first scenario, you return an explicit function(){...} so your array will contain a function that you can call with operator "()".
In the second scenario, in which you return $(<...>).append(), it's not guaranteed anymore that your return value is a function. In that case, the result is "jQuery" (see: jQuery append) and not a function, hence the error is correct. You cannot use the value as a function.
One way out is:
arr2.push(func);
Then:
arr2[c](c);
Or probably this:
(function(x){
arr2[x](x);
})(c);
If needed, it can be helpful to always console.log what is populated inside your arrays before assuming it right. Since Javascript is not a typed language, you may go for a little while before suspecting anything wrong as it will seem work as intended.
Hope this helps!
Related
This question already has answers here:
What is the difference between call and apply?
(23 answers)
Closed 4 years ago.
Dear fellow programmers
I'm dealing with a atrange issues I've been trying to debug for the last two hours. I'm writing a web front end that receives commands from a server that are executed using the window.apply function. A triggerEvents function is used to handle all the different command and passes the arguments sent from the server to the appropriate function (see below code).
function triggerEvents(commands){
for (var index = 0; index < commands.length; index++){
var command = commands[index];
console.debug("execute");
console.debug(command);
console.debug(command.arguments);
window[command.function].apply(null, command.arguments)
}
}
In general that works pretty well, however, one particular function (next code listing) is passed an array of strings but for some reason, only the first element is available in the function.
function updateAvailableBlocks(args) {
availableBlocks = []
for(i=0; i < args.length; i++) {
availableBlocks[i] = args[i];
}
}
Now, when I inspect the debugger I can confirm that I do receive an array of Strings from the server and that that particular array is passed to window.apply as expected.
But when I step into the execution of the function, I am only able to see the first element.
I made sure that all caches are deleted and that Im not using outdated code, but I can't think of something that would cause this. I'd be very grateful for your sharp minds on this.
Thanks in advance!
Use .call instead of .apply. Apply spreads the arguments of passed array as if they were passed one by one. So if your function doesn't expect variable numbers of arguments and you are not handling them properly then it simply won't work as you would expect.
function foo(arg) {
console.log(arg);
}
const arr = ['a', 'b', 'c'];
foo.apply(null, arr);
foo.call(null, arr);
Or you could use the rest operator with apply to achieve the same effect.
function foo(...arg) {
console.log(arg);
}
const arr = ['a', 'b', 'c'];
foo.apply(null, arr);
Consider the following problem: I want to create an array of functions, each function just prints its index in that array. In Python, it can be done easily with
funcs = []
for i in range(5):
funcs.append(lambda i=i: print(i))
funcs[2]()
# 2
Here we use default argument values as a way to do currying (if I understand the term right).
Prior to ES6, there were no default argument values in Javascript, so currying had to be done in different way. Now we have them and I tried to translate Python to Javascript literally:
funcs = []
for (var i=0; i<5; i++) {
funcs.push(function (i=i) {console.log(i)})
}
# this part pass OK
funcs[2]()
ReferenceError: i is not defined
at Array.<anonymous> (evalmachine.<anonymous>:3:27)
at evalmachine.<anonymous>:1:9
at ContextifyScript.Script.runInThisContext (vm.js:26:33)
at Object.exports.runInThisContext (vm.js:79:17)
at run ([eval]:608:19)
at onRunRequest ([eval]:379:22)
at onMessage ([eval]:347:17)
at emitTwo (events.js:106:13)
at process.emit (events.js:191:7)
at process.nextTick (internal/child_process.js:752:12)
Why it fails? What's the difference between Python and Javascript ways to pass default values?
(Okay, I know that I can use let here instead of var, I'm just studying Javascript after several years with Python and trying to understand it underhoods.)
Your issues have to do with the difference between when default parameters get bound in closures in python vs JavaScript. While it is true that both JavaScript and Python use late-binding, in the case of default parameters, Python simulates early-binding, whereas JavaScript does not.
That being said, if you are going to be creating closures like this, you may as well take advantage of them and ditch the parameters all together, honestly.
You mentioned the use of let and that is important if you want to define the function inside the for loop because otherwise funcs[n] will always be your maximum value of your iterator (due to the late-binding of JavaScript closures).
Try this:
funcs = [];
for (let i=0; i<5; i++) {
funcs.push(function () {console.log(i)});
}
funcs[2]();
Alternatively, if you want to follow good practice of not defining functions within a loop, you can define the function outside, and pass the variable in using .bind(). One thing to note is that this method will bind the variable with the value at the time .bind() is called, so you do not have to use let
funcs = [];
function myFunc(i) {
console.log(i);
}
for (var i=0; i<5; i++) {
funcs.push(myFunc.bind(this, i));
}
funcs[2]();
Both Javascript and Python using late-binding in closures. However, using a default argument is a hack that let's you simulate early binding in Python, and that works because default parameters are evaluated at function definition time in Python. However, in Javascript default arguments, according to the docs
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters
The default argument gets evaluated at call time, so unlike e.g. in
Python, a new object is created each time the function is called.
Here is one of the solutions used in Python that can be applied to this problem in Javascript. I've transliterated into Javascript the best I could. Essentially, define another anonymous function that returns your original, and apply it at the same time. This is very messy to my eyes, and in Python, I always go with the default-argument:
funcs = []
for (var i = 0; i < 5; i++) {
funcs.push((function (i) {return function() {console.log(i)}})(i))
};
funcs[0]() // 0
funcs[4]() // 4
In Python:
>>> funcs = []
>>> for i in range(5):
... funcs.append((lambda i: lambda : print(i))())
...
>>> funcs[0]()
0
>>> funcs[4]()
4
I think it is clear that you should use .bind in Javascript, as elucidated in other answers, and not this clunky solution.
If you use let instead of var you get a new binding for each iteration.
funcs = []
for (let i=0; i<5; i++) {
funcs.push(function (j=i) {console.log(j)})
}
funcs[2];//2
i=i doesn't work because in ES6 parameters can define defaults using other parameters
f = function(a=1, b=a){console.log(b);}
f() // 1
So the parser is getting confused.
I'm going through Eloquent JavaScript book and am on chapter 5 (Higher-Order Functions) currently. I'm doing good so far and have understood material clearly, but I've come to realize that I don't understand what function values are exactly and how they work. I do understand what functions are and am quite comfortable with the syntax of creating them using the function keyword as you would do in C for example.
Assume the following code from the book:
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
var numbers = [1, 2, 3, 4, 5],
sum = 0;
forEach(numbers, function(number) {
sum += number;
});
console.log(sum);
How does the forEach function, when called, determine what is the number? How does it extract number from the numbers array. I do understand that action argument in the definition of forEach function "hooks" the action to the element which is currently pointed by the for loop, but I interpret the code as follows:
function(number) {sum += number;}action(array[i])
which doesn't make much sense.
If you could shed light on this issue I'd be more than thankful and also explain what the function value is exactly? How does it differ from function and/or function return value?
Thank you.
From what I've understood, I think that you are being confused by the JavaScript callback function.
In javascript, a function like this:
Look at your code:
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
forEach(numbers, function(number) {
...
For each item of the array (first argument, accessed via array[i]) you are calling action with that value, action(array[i]).
In forEach(numbers, function(number) {, you have passed action (the second argument) as a function with one input. This is the function being called by forEach with each array element. With each pass of the for loop within forEach, this function is called with the new value.
I think trying this with other examples (just start your js console now and begin messing around) will help you to learn this concept.
If I made a function
function run(callback){
callback();
}
and called it like so:
run(function(){
console.log('foo bar baz');
});
I'd expect to see "foo bar baz" printed to the console.
If I now make a new function, and call it,
function withTheAnswer(callback){
callback(42);
}
withTheAnswer(function(theAnswer){
alert('The answer is ' + theAnswer);
});
I'd expect 42 to be alerted.
Honestly, as you start to learn more about JS, you'll see this used a lot, and therefore fully understand it.
I don't understand what function values are exactly and how they work
A function (in JavaScript) is an object that can be called. A function call is nothing special, it is just any expression (like a variable evaluation) followed by a pair of parenthesis with arguments in between.
You could write
var addToSum = function(number) {
sum += number;
};
forEach(numbers, addToSum);
or
function addToSum(number) {
sum += number;
}
forEach(numbers, addToSum);
and it means basically the same: You have a function object, a variable addToSum that refers to it, and it is passed to the forEach call.
How does the forEach function, when called, determine what is the number?
It doesn't know anything about the numbers. All the forEach function sees are an array value (referred to by the parameter name array) and a function value (referred to by the parameter name action). Then it loops over that array, and calls the action function with each of the elements. Only inside the called function this array element is know as number.
I interpret the code as follows: function(number) {sum += number;}action(array[i]) which doesn't make much sense.
If you expand the variable to its value, the variable name disappears:
(function(number) {sum += number;})(array[i]);
which does actually make sense - as I've written above, a function call is just an expression followed by parenthesis. And that expression needs to evaluate to a callable object, which this function expression does.
You might also rewrite it as
function action(number) { sum += number; }
action(array[i]);
if that make more sense to you.
action attribute is a function. This function doesn`t called until you put: 'action([a number])'. Then inside the for loop for each element of array attribute is called the function action with corresponding number.
As #theonlygusti says this function are called callbacks.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
javascript for loop unexpected behaviour
I'am programming in node.js and i run into a small problem what is caused by some strange pass by reference problem.
i use the following code:
for(j in groups){
console.log(j, somefunction(groups[j]))
}
inside of the function some funky things are done with the data but afterwards i get the following as result:
3 data
3 data
3 data
3 data
3 data
3 data
instead of
1 data
2 data
3 data
4 data
5 data
6 data
but it keeps the correct amount of result..
Your quoted code doesn't do what you've listed, you probably lost something simplifying it for the question.
This code would have the effect you're describing, though:
for(j in groups){
doSomething(function() {
// Callback for `doSomething`
console.log(j, somefunction(groups[j]));
});
}
Note that now I'm creating a function (a callback for whatever doSomething does), not just calling one.
The reason that happens is that the function being created has an enduring reference to the j variable (and everything else in the execution context), not a copy of it as of when the function was created. It's called a closure over that execution context. So your loop runs, a series of calls to doSomething start a series of things, and then later when the callbacks are called, j is 3 and so it's the same for all of them.
The usual solution is to create functions that close over something that won't change before they get called, like this:
for(j in groups){
doSomething(makeCallback(j));
}
function makeCallback(jarg) {
return function() {
console.log(jarg, somefunction(groups[jarg]));
};
}
Now, we're using jarg in our callback, rather than j. Because jarg is part of the context of the call to makeCallback, it doesn't change between when we create the function and when it's called later.
Another way you can bind data to functions is to use Function#bind (which effectively creates a closure in the background), which is an ES5 feature available in NodeJS because the V8 engine has it. Here's how that would look:
for(j in groups){
doSomething(function(jarg) {
// Callback for `doSomething`
console.log(jarg, somefunction(groups[jarg]));
}.bind(this, j)); // <== Note the bind
}
or less confusingly (and quite possibly more efficiently):
for(j in groups){
doSomething(handler.bind(this, j));
}
function handler(jarg) {
// Callback for `doSomething`
console.log(jarg, somefunction(groups[jarg]));
}
More about closures:
Closures are not complicated
Do it this way :-
for(var j in groups)
{
//Your code
}
j is not returning the correct value because you have assigned j at a global scope. Hope this help!
ive found if i use
var j = 0
for(i in groups){
j = i
the system does work how but it's not the best awnser
I am completely perplexed. I have an object containing a global "hashed" array of numbers (in objectA) that is referred in a loop that combines the numbers into a new series (in objectB).
var objectB = objectA[arrActive[0]];
for (i=1; i<arrActive.length; i++) {
var _this = arrActive[i];
for (x=0; x<objectB.length; x++) {
objectB[x][1] += objectA[_this][x][1];
}
}
What's weird is that the values in objectA, the source array, are being incremented during the loop - but why? As far as I'm aware, I'm just reading from objectA to write to objectB!
This is frustrating because every time the function is called, the numbers are further inflated!
Working example on JSFiddle is here: http://jsfiddle.net/ZbWGH/ - have I completely misunderstood the += operator? I'm sure this is a simple issue to understand.
Thanks in advance for any help!
You're putting reference to the instance objectA['ONE'] in variable called objectB - any change in that variable will indeed change the actual value.
Instead you might be interested in getting clone or "clean copy" of the array into objectB and this way it won't change the original array.
Simple function that will do this is:
function CopyArray(arr) {
var clone = [];
for (var i = 0; i < arr.length; i++) {
var subArray = [];
for (var j = 0; j < arr[i].length; j++)
subArray.push(arr[i][j]);
clone.push(subArray);
}
return clone;
}
And to use it:
var objectB = CopyArray(objectA[arrActive[0]]);
Updated jsFiddle: http://jsfiddle.net/yahavbr/ZbWGH/1/
Further more A += B is like A = A + B, so you modify objectA.
Do you know C? References/pointers in C are a good way to understand komplex variables in Javascript. "Komplex" meaning everything that is not Number, String, Boolean - everything else is "Object". Variables for the komplex types (Objects) are indeed like pointers. If you know the concepts of "call by reference" and "call by value", in Javascript it's neither, sort of: If you give objects to functions the "pointer" itself is call by value, but the value is a reference to the object (really to the area on the heap where the object is stored, even though JS programmers don't handle heap like in C/C++ it still is where stuff is stored). Example:
function fn (a) {
//changing the argument itself does NOT change the original object
a = null;
//but changing its properties does:
a.foo = 42;
}
var o = { foo:1, bar:2 };
fn(o);
So now it should become clear why you have to clone an object if you want real "call by value". This implementation was chosen for JS because otherwise every single time a function is called with a non-primitive type the heap would have to be copied over, and 99% of the time that just is not necessary. The "true" spirit of functional programming would of course be pure call by value, here we see practical life (performance and memory usage) considerations intruding upon theory :)