Titanium JIRA Archive
Appcelerator Community (AC)

[AC-1898] Titanium Scope Issues

GitHub Issuen/a
TypeBug
Priorityn/a
StatusClosed
ResolutionInvalid
Resolution Date2012-11-21T00:45:56.000+0000
Affected Version/sn/a
Fix Version/sn/a
Componentsn/a
Labelsn/a
ReporterJoel Koett
AssigneeMauro Parra-Miranda
Created2012-11-13T21:06:51.000+0000
Updated2016-03-08T07:40:49.000+0000

Description

Titanium seems to be overwriting loop variables even if used within the inner function. Example:
var window = Ti.ui.createWindow({height: 400, width: 329});
var test = ['This', 'is', 'a', 'test'];
var top = 0;
for (var x in test) {
	var view = Ti.UI.createView({top: top, left: 0, width: 320, height: 10, backgroundColor: 'blue'});
	var current = test[x];
	top += 15;
	view.addEventListener('click', function() {
		alert(current);
	});
	window.add(view);
}
window.open();

You would expect each view to alert its corresponding word, but they all alert 'test'. The workaround so far is to add that property to the view itself:
var window = Ti.ui.createWindow({height: 400, width: 329});
var test = ['This', 'is', 'a', 'test'];
var top = 0;
for (var x in test) {
	var view = Ti.UI.createView({top: top, left: 0, width: 320, height: 10, backgroundColor: 'blue', text: test[x]});
	top += 15;
	view.addEventListener('click', function() {
		alert(this.text);
	});
	window.add(view);
}
window.open();

I should also note that if I use "alert(view.text)" instead of "alert(this.text)", it still always says "test" (for the same reason, except the view variable).

Comments

  1. Daniel Sefton 2012-11-21

    "current" is being created in the global scope, hence why it gets replaced every time. A for-loop is not a function, it does not create its own scope. This is how Javascript works, it's not a Titanium issue.
  2. Bryan Hughes 2012-11-21

    As Daniel mentioned, JavaScript does not have block level scope, so view and current are actually global scope, not for-in scope. The callback function won't be executed until the current thread of execution has finished, meaning that the view and current variables will be whatever their last value was ('test' in this case since the variable is global scope). Your workaround is one way of doing it, although I would recommend another:
       var window = Ti.ui.createWindow({height: 400, width: 329}),
           test = ['This', 'is', 'a', 'test'],
           top = 0,
       test.forEach(function (current) {
           var view = Ti.UI.createView({top: top, left: 0, width: 320, height: 10, backgroundColor: 'blue'});
           top += 15;
           view.addEventListener('click', function() {
               alert(current);
           });
           window.add(view);
       });
       window.open();
       
    The forEach creates a new context for each element in test and "locks" the value of current in place. A bit of advice: always declare your variables at the top of a function/module (C style) so that you avoid logic errors like this one. For a more in-depth discussion of variable hoisting (the whole variables declared at top thing), check out http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting
  3. Mauro Parra-Miranda 2013-11-24

    Invalid issue.

JSON Source