Hard time understanding javascript example - javascript

I am currently following a javascript course and am having issues understanding what is happening behind the scenes in javascript in one of the examples (see code below).
I understand most of the code and understand why the output logged is -> [false, true, true]. However there is one part which is driving me nuts (I pointed an arrow to it in the code at the bottom):
my confusion revolves around the parameter 1:
what journey does the parameter 1 take from the moment it gets passed with checkPastLimitSimplified(1) in var arr5 = mapForEach(arr1, checkPastLimitSimplified(1));.
I understand that when checkPastLimitSimplified(1) is invoked an execution context is created for this function in which the parameter 1 is in the variable environment.
But now what happens?
The function inside the checkPastLimitSimplified function is not executed yet but just returned. What does it look like when it is returned? at what point do the limiter variables receive the parameter 1?
I understand that .bind(this, limiter); creates a copy of the function. Is its limiter variable already a 1 before it gets returned?
function mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(
fn(arr[i])
)
};
return newArr;
}
var arr1 = [1, 2, 3];
var checkPastLimitSimplified = function(limiter) { // < ----CONFUSED
return function(limiter, item) {
return item > limiter;
}.bind(this, limiter);
};
var arr5 = mapForEach(arr1, checkPastLimitSimplified(1));
console.log(arr5);

Lets rename variable to see relations:
var checkPastLimitSimplified = function(outer_limiter) {
return function(limiter, item) {
return item > limiter;
}.bind(this, outer_limiter);
};
bind modifies function signature to be just function(item) before return.
When client code will call checkPastLimitSimplified(1)(item), limiter will substitute from binded context.

another way to make it more understandable is putting the inner function outside:
var checkPastLimit = function(limiter, item) {
return item > limiter;
};
var checkPastLimitSimplified = function(outer_limiter) {
return checkPastLimit.bind(this, outer_limiter);
};
The result will be a copy of the first function with the first parameter (limiter) already defined.
This new function will need only the second param (item).
But there is no execution of the function code (the comparison with the limit) on this stage.

Related

forEach is not defined -- why?

In Eloquent JavaScript, 1st ed, page 77, it gives an example of building a mapping function from scratch:
function mapFunc(func, array) {
var result = []; // not touching the original!
console.log("array:", array)
forEach(array, function(element) {
result.push(func(element));
});
return result;
}
Seems pretty straightforward but when it's run like this:
console.log(mapFunc(Math.round, [0.01, 2, 9.89, Math.PI]))
It throws an error:
ReferenceError: forEach is not defined
Even when I change things up to match the es6 syntax more, same issue:
array.forEach(function(element) {
result.push(func(element))
console.log(element)
})
I've been messing with it for a while and can't figure out what the problem might be or why forEach suddenly becomes undefined. Thoughts?
You need to use array.forEach, and you need to call mapFunc() correctly. You're passing the array as the second argument to console.log() rather than mapFunc().
function mapFunc(func, array) {
var result = []; // not touching the original!
console.log("array:", array)
array.forEach(function(element) {
result.push(func(element));
});
return result;
}
console.log(mapFunc(Math.round, [0.01, 2, 9.89, Math.PI]));
The book defines a forEach() function of its own earlier in the chapter.
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
If you add that function to your code, you should be able to use the mapFunc() function as you first wrote it. But you need to call mapFunc() correctly in either case.

Javascript array only running function that returns a promise on one element of array

Ive posted other questions but feel I should simplify things
I have one function that sets a context and calls a second function to draw the lines on the context.
I have this:
var arr = [];
which is populated like this:
arr = [context,pre];
while pre looks like pre = [[{x:n,y:m}],[{x:j,y:k}]];
So, basically, I have an array, pre, containing arrays of coordinates. That array is pushed with a context into arr
arr is returned and pushed into a final array, lets say final_arr, which now should look like this: final_arr = [[context1,pre1],[context2,pre2],...]
My goal is to loop through final_arr and draw lines on different contexts, determined by the context in the array. For example, the first iteration will access final_arr[0] and contain context1,pre1. These two values are sent to a function, wrap(context, pre) that returns a promise. Inside this wrap function, another function is called, animate(pre[i]). this function takes each element in pre, which corresponds to an array of coordinates, and actually draws the line using animation frames. animate() also returns a promise.
Currently, only one of the paths is being drawn, which seems to be because only one value of final_arr is being used, even though I am iterating through it
My attempts to iterate:
final_arr.reduce((a,c) => a.then(() => wrap(c[0],c[1])), Promise.resolve());
and
var temp = Promise.resolve();
var i = 0;
for (i = 0; i < arr.length; i++){
//window.alert(arr[i].length)
var ct = arr[i][0];
var line = arr[i][1];
temp.then(() => wrap(ct,line));
}
and here are the functions being called:
/*
* Animation function draws a line between every point
*/
var animate = function(p){
return new Promise(function(resolve) {
t = 1;
var runAnimation = function(){
if(t<p.length){
context.beginPath();
context.moveTo(p[t-1].x,p[t-1].y);
context.lineTo(p[t].x,p[t].y);
context.stroke();
t++;
requestAnimationFrame(function(){runAnimation()});
} else {
resolve()
}
};
runAnimation();
});
}
function wrap(ctx, lines){
return new Promise(function(resolve) {
var counter = 0;
t = 1;
var getAnimation = function(){
if(counter < lines.length){
context = ctx;
lines.reduce((a, c) => a.then(() => animate(c)), Promise.resolve());
counter++;
} else {
resolve()
}
};
getAnimation();
});
}
The context variable set in wrap is a global variable for the js file
I hope the question asked this way provides clarity as to what I am having a problem with
Thank you for any help
Edit:
Attempted fiddle
Edit2:
Oddly enough this works
if(final_arr.length == 1){
wrap(final_arr[0][0], final_arr[0][1]);
} else if (final_arr.length == 2){
wrap(final_arr[0][0], final_arr[0][1]).then(wrap(final_arr[1][0], final_arr[1][1]));
} else if (final_arr.length == 3){
wrap(final_arr[0][0], final_arr[0][1]).then(wrap(final_arr[1][0], final_arr[1][1])).then(wrap(final_arr[2][0], final_arr[2][1]));
}
But when using this, the lines are drawn at the same time (which is okay, but not preferred)
edit: just spotted the missing resolve inside the if statement of wrap => the returned Promise will never be resolved...
I recommend to start with a much simpler version of wrap before making any micro-optimizations:
function wrap(ctx, lines){
return new Promise(function(resolve) {
lines.forEach(p => animate(p, ctx));
resolve();
});
}
callbacks to requestAnimationFrame are called after finishing all microtasks (i.e. after all Promises) - see When will requestAnimationFrame be executed?
so the value of the global variable context will be the same for all of the callbacks, i.e. the same line drawn multiple times or a race condition or something depending on internals of the context
I would get rid of globals, using only function params and locals:
var animate = function(p, ctx) {
var t ...
... ctx.beginPath()

setInterval calling function with an undefined parameter

This question has been flagged as already answered with a link provided above. However, I already read that answer and it only answered how to use setInterval in a for loop. There were no functions being called with parameters passed to them in that solution, and that is my situation, so I couldn't use it to fix my situation.
I'm fairly new to programming, so I'll try to describe as best as I can. In setInterval, I am passing a parameter to the function toggleClusters which setInterval calls. The debugger shows the parameter as being correct. It is a reference to an array position that holds an object literal that contains map marker objects. I seem to be misunderstanding something about what values stay around and what do not when using setInterval, because the debugger shows the correct object literal being passed as an arg, but when the function is called, the debugger shows the obj that is supposed to be passed as undefined. Is it that this passed value no longer exists when the function is called?
function setClusterAnimations() {
for (var i = 0; i < clusters.length; i++) {
//intervalNames stores handle references for stopping any setInterval instances created
intervalNames.push(setInterval(function () {
//clusters[i] will hold an object literal containing marker objects
toggleClusters(clusters[i]);
}, 1000));
}
}
//cObj is coming back as undefined in debugger and bombing
function toggleClusters(cObj) {
var propCount = Object.keys(cObj).length;
for (var prop in cObj){
if (prop.getZIndex() < 200 || prop.getZIndex() == 200 + propCount) {
prop.setZIndex(200);
}
else {
prop.setZindex(prop.getZIndex() + 1)
}
}
}
This is typically the issue with such asynchronous calls as with setInterval(). You can solve this in different ways, one of which is using bind():
for (var i = 0; i < clusters.length; i++) {
//intervalNames stores handle references for stopping any setInterval instances created
intervalNames.push(setInterval(function (i) {
//clusters[i] will hold an object literal containing marker objects
toggleClusters(clusters[i]);
}.bind(null, i), 1000));
}
The toggleClusters(clusters[i]) statement will only be executed when your loop has finished, at which time i will be beyond the correct range (it will be clusters.length). With bind(), and mostly with the function parameter i, you create a separate variable in the scope of the call back function, which gets its value defined at the moment you execute bind(). That i is independent from the original i, and retains the value you have given it via bind().
that is because your "i" variable is not captured in the function passed as an argument to setInverval.
Therefore , when this function is invoked, i is always equal to clusters.length.
consider the differences between the two following pieces of code:
var arr = [1, 2, 3];
var broken = function() {
for(var i = 0; i < arr.length; ++i) {
setInterval(function() {
console.log("broken: " + arr[i]);
}, 1000);
// logs broken: undefined
}
};
var fixed = function() {
for(var i = 0; i < arr.length; ++i) {
setInterval((function(k) {
return function() {
console.log("fixed: " + arr[k]);
}
}(i)), 1000); // i is captured here
}
};

how can I get the value of a variable in a closure in javascript [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I'm creating an array of callbacks like this:
function createFunctions(n) {
var callbacks = [];
for (var i=0; i<n; i++) {
callbacks.push(function() {
return i;
});
}
return callbacks;
}
And, here's the test that I'm working with:
var callbacks = createFunctions(5);
for (var i=0; i<callbacks.length; i++) {
Test.assertEquals(callbacks[i](), i, 'Function with index ' + i);
}
Basically, I want the callback to return it's index in the array (i.e. callback[1]() should return 1). But, i defined in createFunctions is set to 6 when the test runs so they always return 6. I've tried creating a local variable to hold the value in the anonymous function but that's not working either.
Any ideas how to make this work?
A closure has an enduring reference to the variables it closes over, not a copy of their value as of when it was created. That's why all of your functions see 6: That's the value that i has as of when those functions are called.
If you need them to see different values, make the closures close over something else that doesn't change. Here's one way:
function createFunctions(n) {
var callbacks = [];
for (var i=0; i<n; i++) {
callbacks.push(makeCallback(i));
}
return callbacks;
function makeCallback(index) {
return function() {
return index;
};
}
}
Or:
function createFunctions(n) {
var callbacks = [];
for (var i=0; i<n; i++) {
callbacks.push(makeCallback(i));
}
return callbacks;
}
function makeCallback(index) {
return function() {
return index;
};
}
(In this example, it doesn't matter whether makeCallback is within createFunctions or outside it.)
Now, the callbacks close over the context containing the index argument. They each get their own copy of that context (which is created by calling the makeCallback function), and so since nothing ever changes index, its value remains unchanged.
More (on my blog): Closures are not complicated
This feature of closures closing over the context of the variable is very, very useful. Consider:
function allocator(start) {
return function() {
return ++start;
};
}
var f = allocator(0);
alert(f()); // "1"
alert(f()); // "2"
alert(f()); // "3"
That wouldn't work if the function created by allocator had a copy of the value of start. It works because the function created by allocator has a reference (through the context) to the variable itself.

Javascript Function Scoped For Loops

Here's an example of a situation where a simple JS loop does not behave as expected, because of the loop variable not being in a separate scope.
The solution often presented is to construct an unpleasant-looking bit of loop code that looks like this:
for (var i in obj) {
(function() {
... obj[i] ...
// this new shadowed i here is now no longer getting changed by for loop
})(i);
}
My question is, could this be improved upon? Could I use this:
Object.prototype.each = function (f) {
for (var i in this) {
f(i,this[i]);
}
};
// leading to this somewhat more straightforward invocation
obj.each(
function(i,v) {
... v ...
// alternatively, v is identical to
... obj[i] ...
}
);
when I ascertain that I need a "scoped loop"? It is somewhat cleaner looking and should have similar performance to the regular for-loop (since it uses it the same way).
Update: It seems that doing things with Object.prototype is a huge no-no because it breaks pretty much everything.
Here is a less intrusive implementation:
function each (obj,f) {
for (var i in obj) {
f(i,obj[i]);
}
}
The invocation changes very slightly to
each(obj,
function(i,v) {
... v ...
}
);
So I guess I've answered my own question, if jQuery does it this way, can't really go wrong. Any issues I've overlooked though would warrant an answer.
Your answer pretty much covers it, but I think a change in your original loop is worth noting as it makes it reasonable to use a normal for loop when the each() function isn't handy, for whatever reason.
Update: Changed to use an example that's similar to the example referenced by the question to compare the different approaches. The example had to be adjusted because the each() function requires a populated array to iterate over.
Assuming the following setup:
var vals = ['a', 'b', 'c', 'd'],
max = vals.length,
closures = [],
i;
Using the example from the question, the original loop ends up creating 2n functions (where n is the number of iterations) because two functions are created during each iteration:
for (i = 0; i < max; i++) {
closures[i] = (function(idx, val) { // 1st - factoryFn - captures the values as arguments
return function() { // 2nd - alertFn - uses the arguments instead
alert(idx + ' -> ' + val); // of the variables
};
})(i, vals[i]);
}
This can be reduced to creating only n + 1 functions by creating the factory function once, before the loop is started, and then reusing it:
var factoryFn = function(idx, val) {
return function() {
alert(idx + ' -> ' + val);
};
};
for (i = 0; i < max; i++) {
closures[i] = factoryFn(i, vals[i]);
}
This is nearly equivalent to how the each() function might be used in this situation, which would also result in a total of n + 1 functions created. The factory function is created once and passed immediately as an argument to each().
each(vals, function(idx, val) {
closures[idx] = function() {
alert(idx + ' -> ' + val);
};
});
FWIW, I think a benefit to using each() is the code is a bit shorter and creating the factory function right as it's passed into the each() function clearly illustrates this is its only use. A benefit of the for loop version, IMO, is the code that does the loop is right there so it's nature and behavior is completely transparent while the each() function might be defined in a different file, written by someone else, etc.
Global Scope
When something is global means that it is accessible from anywhere in your code. Take this for example:
var monkey = "Gorilla";
function greetVisitor () {
return alert("Hello dear blog reader!");
}
If that code was being run in a web browser, the function scope would be window, thus making it
available to everything running in that web browser window.
Local Scope
As opposed to the global scope, the local scope is when something is just defined and accessible in a
certain part of the code, like a function. For instance;
function talkDirty () {
var saying = "Oh, you little VB lover, you";
return alert(saying);
}
alert(saying); // Throws an error
If you take a look at the code above, the variable saying is only available within the talkDirty
function. Outside of it it isn’t defined at all. Note of caution: if you were to declare saying without
the var keyword preceding it, it would automatically become a global variable.
What this also means is that if you have nested functions, the inner function will have access to the
containing functions variables and functions:
function saveName (firstName) {
function capitalizeName () {
return firstName.toUpperCase();
}
var capitalized = capitalizeName();
return capitalized;
}
alert(saveName("Robert")); // Returns "ROBERT"
As you just saw, the inner function capitalizeName didn’t need any parameter sent in, but had complete
access to the parameter firstName in the outer saveName function. For clarity, let’s take another
example:
function siblings () {
var siblings = ["John", "Liza", "Peter"];
function siblingCount () {
var siblingsLength = siblings.length;
return siblingsLength;
}
function joinSiblingNames () {
return "I have " + siblingCount() + " siblings:\n\n" + siblings.join("\n");
}
return joinSiblingNames();
}
alert(siblings()); // Outputs "I have 3 siblings: John Liza Peter"
As you just saw, both inner functions have access to the siblings array in the containing function, and
each inner function have access to the other inner functions on the same level (in this case,
joinSiblingNames can access siblingCount). However, the variable siblingsLength in the siblingCount is
only available within that function, i.e. that scope.

Categories