function is overriding variable declared in other scope - javascript

I have to functions both using i as an iterator.
One function is using the other.
I thought local Variables cant be changed by other scopes.
But now "i" is changed after the "createOptions" function.
Is it possible that the value is passed by the object?
function createOptions(array) {
var optionArray = [];
for (i=0;i<array.length;i++) {
var option = $("<option></option").text(array[i]);
$(option).attr("value", array[i]);
optionArray.push(option);
}
return optionArray;
}
function buildForm(players) {
var form = $("<form></form>");
for (i=0; i<4; i++) {
var playernumber = "player " + (i+1).toString();
var select = $("<select></select>").attr({
"id" : playernumber,
"onchange" : "addNewPlayer(this)"
});
var selectPlayerOption = $("<option></option>").text("-- select Player --");
$(selectPlayerOption).attr({
"disabled" : true,
"selected" : true
})
var newPlayerOption = $("<option></option>").text("New Player");
var playerOptions = createOptions(players);
console.log(playerOptions.j);
$(select).append(selectPlayerOption, newPlayerOption, playerOptions);
$(form).append(select);
}
$("#maincontent").append(form);
}

If you create the variable like i = 0 even if it is inside a for loop still will be created as global variable.
Suggested way instead:
for (let i=0; i < array.length; i++) {
// your code
}
Read further here about let.
I hope that helps!

that is normal your function buildForm is using i as an iterator and when CreateOption is called inside buildForm function it value is reassigned to 0 because of this line for (i=0;i<array.length;i++) in createOption function.
So unless you want to use i in both functions i suggest to use two differents iterators i and j for example

Related

How to fix scope issues when using an onclick event in a for loop [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 3 years ago.
I am curretly experiencing difficulties implementing an onclick event within a for loop. Instead of alerting the respective value it always returns undefined (presumably a scope problem, because the iteration itself works fine)
Until now I tried to pass on the i variable to the onclick function; however, with little success
for (var i = 0; i < timeSpanLength; i++) {
// creating the wrap for the month
var month = document.createElement("div");
month.className = 'month_element';
var reference_month = document.createElement("span");
reference_month.innerHTML = time_span[i];
//onclick event
reference_month.onclick = function(i) {
var month_beginning = signup_date;
var month_end = time_span[i];
alert(month_end);
//searchForData(month_beginning, month_end);
};
//append to container
month.appendChild(reference_month);
document.getElementById('time_container').appendChild(month);
}
The expected outcome is to trigger an alert which displays the same month which is displayed in the span element above. I need the variable to pass it on to another function.
Any help is highly appreciated since I am beginner in javascript.
for (var i = 0; i < timeSpanLength; i++) {
(function (index) {
// creating the wrap for the month
var month = document.createElement("div");
month.className = 'month_element';
var reference_month = document.createElement("span");
reference_month.innerHTML = time_span[index];
//onclick event
reference_month.onclick = function() {
var month_beginning = signup_date;
var month_end = time_span[index];
alert(month_end);
//searchForData(month_beginning, month_end);
};
//append to container
month.appendChild(reference_month);
document.getElementById('time_container').appendChild(month);
})(i);
}
This callback function handler is forming a closure with respect to the outer scope. Also var has a function scope, so in essence the block of code can be re-written as:
var i;
for (i = 0; i < timeSpanLength; i++) {
...
//onclick event
reference_month.onclick = function(i) {
var month_beginning = signup_date;
var month_end = time_span[i];
alert(month_end);
//searchForData(month_beginning, month_end);
};
...
}
So the var i is hoisted to the top and when the loop completes the value of i is timeSpanLength.length and this is what you use to access time_span[i] and that returns undefined.
Since with var the binding remains the same, the handlers registered will be referring the last value of i in the loop.
So you either need to use let in the for-loop:
for (let i = 0; i < timeSpanLength; i++) { ... }
Or an IIFE which forms a new scope bound to each new value of i from the loop:
for (var i = 0; i < timeSpanLength; i++) {
(function(i){
reference_month.onclick = function(i) {
var month_beginning = signup_date;
var month_end = time_span[i];
alert(month_end);
//searchForData(month_beginning, month_end);
};
})(i)
}

Outer Variable scope is not available inside the javascript function

How to access the outer variable inside the inner javascript function , please check my code below i need to access "index" variable inside app() function .
function validate()
{
for (index = 0; index < results.length; index++) {
var parsedData_1;
var provider = results[index].get("provider");
var user = results[index].get("user");
var addresses = user.get("addresses");
var address = addresses[1];
var GameScore = Parse.Object.extend("Address");
var query = new Parse.Query(GameScore);
var data = JSON.stringify(address);
var parsedData = jQuery.parseJSON(data);
query.get(parsedData.objectId, {
success: function(resultadr) {
var res = JSON.stringify(resultadr);
var parsedData_1 = jQuery.parseJSON(res);
var apartment = parsedData_1.apartment;
apa(apartment);
},
error: function(object, error) {
alert('error');
}
});
function apa(apartment)
{
alert(index); [I'm not getting the index value inside this function]
alert(apartment);
}
}
}
Inside the function apa() i need the index count , but im unable to get it , please help how to access the outer value inside the function.
As far as I can see apa() does not have access to the index variable, so I would put it inside the parameter of the function call, and call apa() like this
apa(apartment, index);
The problem is that you are using closures to access the index, thus the lastest value assinged to that variable is accessible in the app function. For example, if results.length equlas 11, index in app function will always be 11.
The soltion is to declare a variable inside the for loop, assign the index value to that variable, and pass the variable to app function.
//Create a factory function for the app function
//Outside the for loop.
var createAppFunc = function (i) {
return function () {
console.log(i);
};
};
for (var index = 0, l = results.length; index < l; index++)
{
var newVar = index;
var app = createAppFunc(index); //Now, app is a function.
}
Good Luck :).
You can use Function.bind() to pass the actual index as first argument to the sucess-callback. There you can get it and pass it along as parameter to function apa(). And: use keyword var for the index in for().
function validate() {
for (var index = 0; index < results.length; index++) {
/* all the vars here */
query.get(parsedData.objectId, {
success: function(idx, resultadr) {
var res = JSON.stringify(resultadr);
var parsedData_1 = jQuery.parseJSON(res);
var apartment = parsedData_1.apartment;
apa(idx, apartment);
}.bind(null, index),
error: function(object, error) {
alert('error');
}
});
function apa(index, apartment) {
console.log(index); console.log(apartment);
}
}
}
The first parameter (here: null) of bind() sets the context of the bound function. Whatever you pass in will be accessible in the success-callback by the keyword this.
I think every time you call the function the call will be compromised by next loop call.. so at last the method will be called only once.
You can declare index variable globally then all the functions have access to it

Variable scoping and event handler

Please see the jsfiddle:
http://jsfiddle.net/LsNCa/2/
function MyFunc() {
for (var i = 0; i < 2; i++) { // i= 0, 1
var myDiv = $('<div>');
myDiv.click(function(e) {
alert(i); // both the two divs alert "2", not 0 and 1 as I expected
});
$('body').append(myDiv);
}
}
var myFunc = new MyFunc();
I want the divs to alert "0" and "1" respectively when I click them, but both of them alert "2".
When I click the divs and the event is triggered, how and where do the handler find the value of the variable i?
I'm aware that adding a closure achieves my goal. But why?
function MyFunc() {
for (var i = 0; i < 2; i++) { // i= 0, 1
(function(j) {
var myDiv = $('<div>');
myDiv.click(function(e) {
alert(j);
});
$('body').append(myDiv);
})(i);
}
}
var myFunc = new MyFunc();
The code above is how you get it work correctly. Without an closure, you always the the last value of i. What we do is to post i into the closure and let the runtime "remember" the value of that very moment.
You need a closure because all your event handler functions are referencing the same variable i. The for loop updates this, and when the loop is done the variable contains 2. Then when someone clicks on one of the DIVs, it accesses that variable.
To solve this, each event handler needs to be a closure with its own variable i that contains a snapshot of the value at the time the closure was created.
I suggest that you read this article
JavaScript hoists declarations. This means that both var statements
and function declarations will be moved to the top of their enclosing
scope.
As #Barmar said in his answer above, the variable i is being referenced by both the event handlers.
You should avoid declaring functions inside loops. Below there is some code that does what you need.
I assume that you're using jQuery.
function MyFunc() {
for (var i = 0; i < 2; i++) { // i= 0, 1
var myDiv = $('<div>');
$('body').append(myDiv);
}
$('div').on('click', function() {
alert($(this).index());
});
}
var myFunc = new MyFunc();
The "alert()" call happens after the for-loop completed, which means that the value of "i" will be the last value for anything after that. In order to capture individual values of "i", you must create a closure for each value by creating a new function:
function MyFunc() {
function alertFn(val) {
return function () {
alert(val);
};
}
for (var i = 0; i < 2; i++) {
var myDiv = $('<div>');
myDiv.click(alertFn(i));
$('body').append(myDiv);
}
}
var myFunc = new MyFunc();
The closure captures the value of "i" at the time it was passed into the function, allowing alert() to show the value you expect.

Update happens only on the last row, instead of first

function createTextFields(obj) {
for (var i = 0; i < obj.length; i++) {
var dataDump = {};
for (var key in obj[i]) {
var textField = Ti.UI.createTextField(pm.combine($$.labelBrown, {
left: 200,
height:35,
value:obj[i][key],
width:550,
keyboardType:Ti.UI.KEYBOARD_NUMBER_PAD,
layout:'horizontal',
backgroundColor:'transparent',
id:i
}));
dataDump[key] = textField.value;
var callback = function (vbKey) {
return function (e) {
dataDump[vbKey] = e.source.value;
};
}(key);
}
globalData.push(dataDump);
}
}
I am using the simlar code for Adding the data and it works fine. I posted the problem yesterday and it got resolved...
Last Object is always getting updated?
Now when i go to edit page, it shows me four text fields or number of text fields added... now when i edit something and click on save... the value get's updated on the fourth or the last TextFields Object...
Don't define functions inside loops. Computationally expensive and leads to problems, like this one. Here's a fix that should solve it:
function createTextFields(obj) {
var callback = function (vbKey, localDump) {
return function (e) {
localDump[vbKey] = e.source.value;
};
}
var i;
var max = obj.length;
for (i = 0; i < max; i++) {
var dataDump = {};
for (var key in obj[i]) {
dataDump[key] = textField.value;
var callBackInstance = function(keyn, dataDump);
}
globalData.push(dataDump);
}
}
JavaScript does not have block level scope, so your variables dataDump and callback, though "declared" inside for-loops actually belong to the function. As in, you're saving a value to dataDump, then you're overwriting it, each time you go through the loop. Which is why finally only the code that operated on the last value remains.
Take a look at What is the scope of variables in JavaScript? too.

Scope troubles in Javascript when passing an anonymous function to a named function with a local variable

Sorry about the title - I couldn't figure out a way to phrase it.
Here's the scenario:
I have a function that builds a element:
buildSelect(id,cbFunc,...)
Inside buildSelect it does this:
select.attachEvent('onchange',cbFunc);
I also have an array that goes:
var xs = ['x1','x2','x3'...];
Given all of these, I have some code that does this:
for(var i = 0; i < xs.length; i++)
{
buildSelect(blah,function(){ CallBack(xs[i],...) },...);
}
The issue is that when onchange gets fired on one of those selects it correctly goes to CallBack() but the first parameter is incorrect. For example if I change the third select I expect CallBack() to be called with xs[2] instead I get some varying things like xs[3] or something else.
If I modify it slightly to this:
for(var i = 0; i < xs.length; i++)
{
var xm = xs[i];
buildSelect(blah,function(){ CallBack(xm,...) },...);
}
I'm still getting incorrect values in CallBack(). Something tells me this is scope/closure related but I can't seem to figure out what.
I simply want the first select to call CallBack for onchange with the first parameter as xs[0], the second select with xs[1] and so on. What could I be doing wrong here?
I should clarify that xs is a global variable.
Thanks
You need to capture that xm value by closing around it in its own scope.
To do this requires a separate function call:
buildCallback( curr_xm ) {
// this function will refer to the `xm` member passed in
return function(){ CallBack(curr_xm,...) },...);
}
for(var i = 0; i < xs.length; i++)
{
var xm = xs[ i ];
buildSelect(blah,buildCallback( xm ),...);
}
Now the xm that the callback refers to is the one that you passed to buildCallback.
If you have other uses for i that need to be retained, you could send that instead:
buildCallback( curr_i ) {
// this function will refer to the `i` value passed in
return function(){ CallBack( xs[ curr_i ],...) },...);
}
for(var i = 0; i < xs.length; i++)
{
buildSelect(blah,buildCallback( i ),...);
}
The problem is indeed scope-related -- JavaScript has only function scope, not block scope or loop scope. There is only a single instance of the variables i and xm, and the value of these variables changes as the loop progresses. When the loop is done, you're left with only the last value that they held. Your anonymous functions capture the variables themselves, not their values.
To capture the actual value of a variable, you need another function where you can capture the local variable:
function makeCallback(value) {
return function() { CallBack(value, ...) };
}
Each call to makeCallback gets a new instance of the value variable and if you capture this variable, you essentially capture the value:
for(var i = 0; i < xs.length; i++)
{
buildSelect(blah,makeCallback(xs[i]),...);
}
Yes, I think a closure would help:
for(var i = 0, l = xs.length; i < l; i++)
{
buildSelect(
blah,
function(xm){
return function(){
CallBack(xm,...)
};
}(xs[i]),
...
);
}
Edit: I also optimised your for loop slightly.
Edit: I guess I'll add an explanation. What you're doing is creating an anonymous function which takes one argument (xm) and calling the function straight away (with the parenthesis right after). This anonymous function must also return your original function as an argument of buildSelect().
Apparently there is a new let keyword that does what you want:
for(var i = 0; i < xs.length; i++)
{
let xm = xs[i];
buildSelect(blah,function(){ CallBack(xm,...) },...);
}

Categories

Resources