Javascript : Anonymous function, access to global variables - javascript

After hours of search, I Have a problem with my code Below.
In fact, I'm not very far from answer I think but I'm still blocked…
I have an anonymous function called inside a loop and I want to access and refresh global variables but I tried with window.myvariable, with another function and nothing happen…
this my code :
for (var i = 0; i < SHP_files.length; i++) {
shapefile = new Shapefile({
shp: "shp/polygon/"+SHP_files[i]+".shp",
dbf: "shp/polygon/"+SHP_files[i]+".dbf",
}, function(data) {
polygon_layer.addLayer(new L.GeoJSON(data.geojson,{onEachFeature: onEachFeature, style: polygonStyle}));
polygon_layer.addTo(map);
console.log(polygon_layer.getLayers()); // IS OK
});
};
console.log(polygon_layer.getLayers()); // IS EMPTY !!
So, How i could transform this anonymous function in order to have something that I can access from my code who's following that ?
Thanks a lot, and sorry for my english not very good…

This is your typical problem with asynchronous code execution. You example code does NOT execute from top to bottom. In particular, your anonymous function does NOT get executed until Shapefile is done with whatever it is doing. In the meantime, your JS gets executed in order. Therefore, the last line of your above code, will probably execute before the anonymous function ever will.
To fix this, you will need to trigger any code that depends on the Shapefile response from within its callback:
for (var i = 0; i < SHP_files.length; i++) {
shapefile = new Shapefile({
shp: "shp/polygon/"+SHP_files[i]+".shp",
dbf: "shp/polygon/"+SHP_files[i]+".dbf",
}, function(data) {
polygon_layer.addLayer(new L.GeoJSON(data.geojson,{onEachFeature: onEachFeature, style: polygonStyle}));
polygon_layer.addTo(map);
executeMoreCode();
});
};
function executeMoreCode() {
console.log(polygon_layer.getLayers()); // IS OK
}

Try defining your variables, in this case polygon_layer, outside of the for loop or the function. See the following example:
var f;
for(var i=0; i<5;i++){
(function(){
f = 10;
console.log(f); // Outputs 10
})();
}
console.log(f); // Also outputs 10

Related

How do I make this into a self-invoking function?

I'm not sure how to make this function into a self-invoking function. My code is looping through an array of zip codes from a JS file and sorting it from smallest to biggest and outputting it. I've found online that adding "())" at the end of the newZipCodeArray function, is supposed to self-invoke the function. However, it's not working. What am I doing wrong?
[enter image description here][1]
// Global variable
var zipCodeArray = [];
(function newZipCodeArray(currentZipCode) {
var valueFound = false;
for (zipCodeIndex = 0; zipCodeIndex <= zipCodeArray.length; zipCodeIndex++) {
if (currentZipCode === zipCodeArray[zipCodeIndex]) {
valueFound = true;
}
}
if (valueFound === false) {
zipCodeArray.push(currentZipCode);
}
}());
function newZipCodeArrayssignment12_3() {
// Instantiate variables.
var output;
var zipCodeRecords;
// Get the element.
output = document.getElementById('outputDiv');
zipCodeRecords = openZipCodeStudyRecordSet();
// Call the function to read the next record.
while (zipCodeRecords.readNextRecord()) {
currentZipCode = zipCodeRecords.getSampleZipCode();
newZipCodeArray(currentZipCode);
}
// Sort the zip code array.
zipCodeArray.sort();
}
The syntax involved in immediately-invoked (or self-invoked) functions doesn't allow it to be invoked from elsewhere. The IIFE pattern is intended to be used when the function only needs to be invoked once.
Note:
The grouping parenthesis wrapping the function change it from a declaration to an expression. And, as an expression, its name won't be added to the surrounding scope for other code to reference.
To invoke newZipCodeArray once right away and allow for it again later, you'll want to remove the parenthesis from around it and call it by name in a separate statement:
newZipCodeArray(); // first call
function newZipCodeArray(currentZipCode) {
// ...
}
function newZipCodeArrayssignment12_3() {
// ...
while (zipCodeRecords.readNextRecord()) {
// ...
newZipCodeArray(currentZipCode); // additional calls
}
// ...
}

How to stock the data obtained from JSON in variables?

Okay I am a newbie in web developing and it's been hours I am trying to figure out how to stock data into variables in order to use them in other functions.
Here is what I am trying to do :-
(function($){
var Rows;
var Cols;
function initBoard(link) {
$.getJSON(link,function(data){
Rows=data.nRows;
});
$div = $('#container');
for(var i = 0; i < Rows; i++) {....}
......
this is a long function to draw a Grid from the JSON file.So at the end I call
$(document).ready(function() {
initBoard("easy.json");
});
})(jQuery);
And then it draws the thing.
The problem is that it seems that nothing gets stocked into my variable Rows..
Even when I try with alert(Rows), nothing happens.
I tried another version doing this :-
(function($){
var dataR;
function test(link){
$.getJSON(link,function(data){
dataR=data.nCols;
dataC=data.nRows;
alert(dataR); // Gives me 13 as Well, DATA READED and stocked..
});
alert(dataR); // Gives me undefined..
};
function initBoard() { .... //
the function I call to draw the grid, but I want to use the global var
dataR inside..
.
.// functions I use
$(document).ready(function() {
initBoard("easy.json");
test("easy2.json");
});
})(jQuery);
Thank you forgiving me hints or showing help.
Make a global variable in javascript at the top of your code
var _dataToStock;
//your code here
_dataToStock=dataR;//Put this where you get desired value.
Now you can use _dataToStock variable to perform othe actions.
Be sure to check value of _dataToStock for null and undefined before use.
Good question. This is because, you are loading data via asynchronous ajax call and while the data is being loaded, your next line of code is executed, in which you are accessing variable, which has not been initialized yet, because ajax call is still in progress.
How to resolve: Either put your code block in the success callback or wrap your code block in a function and call this function in the success callback of your ajax call. This will results in, you will be able to access the intiaited variable.
Happy Coding :-)
Try This.
var globalVariable = [""];
$(function() {
initBoard(link);
printData();
});
function initBoard(link) {
$.getJSON(link,function(data){
var rlen = data.length;
for (var i = 0; i < rlen; i++)
{
globalVariable.push(data[i]);
}
}
});
function printData(){
try{
for(i = 0;;i++){
try{
if(String(globalVariable[i]) == 'undefined'){
break;
}
}catch(e){
}
alert(globalVariable[i]);
}
}catch(e){
}
}

JavaScript Callback Errors or Doesn't Run

I'm working with an array of cells (in qualtrics surveys) and have tried to write a function called watchSet that you can pass a set of cells and a function to that watches the set of cells for any changes (keyups) and runs the function passed to it again whenever any of that set of cells are changed.
function watchSet(set, mathFunction) {
var setSize = set.length;
for (var i=0; i < setSize; i++) {
set[i].down().observe("keyup", function(){
mathFunction
});
}
}
An example function that uses this is the qualtricsSum function (which also uses the mathSum function)
function mathSum(set, output) {
var setTotal = 0;
for (var j=0; j < (set.length); j++) {
var setInputValue = parseInt(set[j].down().value, 10);
if (isNaN(setInputValue)) { setInputValue = 0; }
setTotal = setTotal + setInputValue;
}
output.down().value = setTotal;
}
function qualtricsSum(array, output) {
watchSet(array, mathSum(array, output));
}
In the watchSet function I wrap the mathFunction I pass with function(){...} and it runs the mathSum function, but doesn't seem to run it on keyups, but if I don't wrap it with the unnamed function, I get Uncaught TypeError: Cannot read property 'call' of undefined as an error. I'm not sure if that's part of my problem.
When I manually run the for loop that's in watchSet and replace mathFunction with the function I intend to run, it does actually run the function I give it every time I edit the cells. This makes me think that somehow calling watchSet(set, mathFunction) and then referencing mathFunction in the function definition doesn't actually pass what I'm thinking it is passing.
EDIT:
I realized once I saw behtgod's answer that I didn't clarify this:
I do not always know what mathFunction's arguments look like, and I would like to be able to pass any function with any number of arguments as the mathFunction. Sometimes it will be functions with a format like mathSum(array, output), other times I'd like it to be mathEqual(array), or any number of different kinds of things.
First,your mathFunction is a function, so you should use it like
mathFunction();
Second,when below line is executed
watchSet(array, mathSum(array, output));
the mathSum have been called and only the result is pass to watchSet function.
So you should use like this:
function watchSet(set, mathFunction) {
var setSize = set.length;
for (var i=0; i < setSize; i++) {
set[i].down().observe("keyup", function(set,output){
mathFunction(set,output)
});
}
}
function mathSum(set, output) {
...
}
function qualtricsSum(array, output) {
watchSet(array, mathSum);
}
because you mathFunction is called in a callback ,so the arguments which the mathFunction used,must be arguments of the callback

Javascript Closures and self-executing anonymous functions

I was asked the below question during an interview, and I still couldn't get my head around it, so I'd like to seek your advice.
Here's the question:
var countFunctions = [];
for(var i = 0; i < 3; i++){
countFunctions[i] = function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
//The below are executed in turns:
countFunctions[0]();
countFunctions[1]();
countFunctions[2]();
When asked what would be the output of the above, I said count0,count1 and count2 respectively. Apparently the answer was wrong, and that the output should all be count3, because of the concept of closures (which I wasn't aware of then). So I went through this article and realized that I should be using closure to make this work, like:
var countFunctions = [];
function setInner(i) {
return function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
for(var i = 0; i < 3; i++){
countFunctions[i] = setInner(i);
}
//Now the output is what was intended:
countFunctions[0]();//count0
countFunctions[1]();//count1
countFunctions[2]();//count2
Now that's all well and good, but I remember the interviewer using something simpler, using a self-executing function like this:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(){
document.getElementById('someId').innerHTML = 'count' + i;
})(i);
}
The way I understand the above code, we are skipping the declaration of a separate function and simply calling and executing the function within the for loop.
But when I ran the below:
countFunctions[0];
countFunctions[1];
countFunctions[2];
It didn't work, with all the output being stuck at count2.
So I tried to do the below instead:
for(var i = 0; i < 3; i++) {
countFunctions[i] = function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
, and then running countFunctions[0](), countFunctions[1]() and countFunctions[2](), but it didn't work. The output is now being stuck at count3.
Now I really don't get it. I was simply using the same line of code as setInner(). So I don't see why this doesn't work. As a matter of fact, I could have just stick to the setInner kind of code structure, which does work, and is more comprehensive. But then I'd really like to know how the interviewer did it, so as to understand this topic a little better.
The relevant articles to read here are JavaScript closure inside loops – simple practical example and http://benalman.com/news/2010/11/immediately-invoked-function-expression/ (though you seem to have understood IEFEs quite well - as you say, they're "skipping the declaration of a separate function and simply calling and executing the function").
What you didn't notice is that setInner does, when called, return the closure function:
function setInner(i) {
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
// then do
var countFunction = setInner("N"); // get the function
countFunction(); // call it to assign the innerHTML
So if you translate it into an IEFE, you still need to create (and return) the function that will actually get assigned to countFunctions[i]:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(i){
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
})(i);
}
Now, typeof countFunctions[0] will be "function", not "undefined" as in your code, and you can actually call them.
Take a look at these four functions:
var argument = 'G'; //global
function passArgument(argument){
alert(argument); //local
}
function noArguments(){
alert(argument); //global
}
function createClosure_1(argument){
return function (){
alert(argument); //local
};
}
function createClosure_2(argument){
var argument = argument; //local
return function (){
alert(argument); //local
};
}
passArgument('L'); //L
noArguments(); //G
createClosure_1('L') //L
createClosure_2('L') //L
alert(argument) //G
I think, first function is obvious.
In function noArguments you reference the global argument value;
The third and fourth functions do the same thing. They create a local argument variable that doesn't change inside them and return a function that references that local variable.
So, what was in the first and the last code snippet of your question is a creation of many functions like noArguments,
that reference global variable i.
In the second snippet your setInner works like createClosure_1. Within your loop you create three closures, three local variables inside them. And when you call functions inside countFunctions, they get the value of the local variable that was created inside the closure when they were created.
In the third one you assign the result of the execution of those functions to array elements, which is undefined because they don't return anything from that functions.

Javascript variable scope question

I'm having trouble resolving a scope issue with my javascript.
I have an array, dog[] that is defined from JSON, that I need access to from inside a nested function.
function blah(json) {
for (var u = 0; u < json[0][1][u].length; u ++ ) {
var dog = 'k' + json[0][1][u].doggies;
console.log(dog); // prints array of doggie strings
$('#puppy').click(function(dog) { // dog is passed in the function
console.log(dog); // Syntax error, unrecognized expression: #[object Object]
$('#' + dog).css('display, 'none');
});
}
}
when I dont pass dog into the click function: i get:
$('#puppy').click(function() {
console.log(dog) // (12) main.js:122k4c812e3a7275e10331000000 - this is the last value in the array - from safari console
$('#' dog).css('display', 'none);
}
Does anyone have any suggestions to get the array with every element passed into the click function?
Or am i calling the css method incorrectly to hide those divs?
Problem 1
Closures bind the entire function's scope, and not individual variables or values.
Take this code for example:
function foo() {
var i, func;
for (i = 0; i < 10; ++i) {
if (i == 0) {
func = function () {
alert(i);
}
}
}
func();
}
foo();
You may expect foo to cause 0 to be alerted. However, the value of i has changed since the function assigned to func was created; the call to func alerts "10".
Here is another example illustrating the concept:
function foo() {
var i = 42;
function func() {
alert(i);
}
for (i = 0; i < 10; ++i) {
// do nothing
}
func();
}
foo();
Try to figure out what will be alerted, and run the code as a test.
Problem 2
The second problem is that variables are bound at the function scope (and not the block scope as you expect).
Take this code:
function foo() {
var i;
for (i = 0; i < 10; ++i) {
var j = i;
}
alert(j);
}
foo();
You may expect this code to alert "undefined", throw a run-time error, or even throw a syntax error. However, "10" is alerted. Why? In JavaScript, the above code is translated into effectively:
function foo() {
var i;
var j;
for (i = 0; i < 10; ++i) {
j = i;
}
alert(j);
}
foo();
It should be more clear from this example that "10" is indeed alerted.
Solution
So how do you fix your problem? The simplest way is to change your logic: instead of attaching one event handler per dog, attack one event handler per collection of dogs. For example:
function blah(json) {
$('#puppy').click(function () {
var u, dog;
for (u = 0; u < json[0][1][u].length; u++) {
dog = 'k' + json[0][1][u].doggies;
console.log(dog);
$('#' + dog).css('display', 'none');
}
});
}
If you're interested in the "proper" transformation of your existing code (i.e. having the same behaviours, except with the bug fixed), I can give you an example of that as well. However, the solution I gave above is a much better solution and results in cleaner code.
Important Note:
You forgot to close your quote. This:
$('#' + dog).css('display, 'none');
Should be:
$('#' + dog).css('display', 'none');
An Improved Loop:
There are several problems with your script. I'll concentrate on the overall logical structure of the loop.
Instead of attaching many handlers to .click(), just attach one handler that iterates over you JSON using jQuery's .each(). The first argument of the callback of .each() is the index number and the second argument is the value. You can make use of those 2 by naming the arguments or by using arguments[0] and arguments[1]. I show the former method below:
I've added some more test output for demonstration purposes:
function blah(json) {
$('#puppy').click(function() {
// iterate over each json[0][1]
$.each(json[0][1], function(index, value) {
// Your original 2 lines
console.log(value);
$('#' + value).css('display', 'none');
// This is just test output, so you can see what is going
// on.
$("body").append("Number " + index + " is " + value ".<br/>");
});
});
}
Why not just give the doggies a class .dog and hide them when #puppy is clicked?
$("#puppy").click(function() {
$(".dog").hide();
});
Or since your dog's IDs seem to start with k, you might consider something like this:
$("#puppy").click(function() {
// hide everything with ID beginning with 'k'
$('[id^=k]').hide();
});
You can't pass the dog value into the jquery click event as you have done there. The click function signature is:
$(object).click(function(){
});
You can't pass dog in like this. Even if the function expected a parameter, naming it dog would cause issues. You may need to store the values of dog in a more global scope so that when the click event occurs, you still have access to it.

Categories

Resources