skaterdav85 Posted January 14, 2011 Share Posted January 14, 2011 I am trying to understand closures and I have created an example which needs it, but I just don't know how to implement it. You can see my JS bin file here:http://jsbin.com/ekoqi4/editBasically there are 5 links, and when you click each one, i want it to alert its position in the collection. Right now each link alerts the number 4 because it is alerting the the last value of the counter variable i. Anyone know how to implement a closure here? <a href="#">One</a> <a href="#">Two</a> <a href="#">Three</a> <a href="#">Four</a> window.addEventListener('load', function () { var anchors = document.getElementsByTagName('a'), i, len; for(i = 0, len = anchors.length; i < len; i++) { this.onclick = function () { alert(i); }; }}, false); Link to comment Share on other sites More sharing options...
justsomeguy Posted January 14, 2011 Share Posted January 14, 2011 One solution: for(i = 0, len = anchors.length; i < len; i++) { anchors[i].setAttribute('num', i); anchors[i].onclick = function () { alert(this.getAttribute('num')); }; } Link to comment Share on other sites More sharing options...
skaterdav85 Posted January 14, 2011 Author Share Posted January 14, 2011 oops i just noticed that i used this incorrectly and it should have been anchors. Anyways, here is the updated jsbin file:http://jsbin.com/ekoqi4/3/editJSG, do you know how to solve this problem using a closure? window.addEventListener('load', function () { var anchors = document.getElementsByTagName('a'), i, len; for(i = 0, len = anchors.length; i < len; i++) { anchors[i].addEventListener('click', function () { alert(i); }, false); }}, false); Link to comment Share on other sites More sharing options...
jeffman Posted January 14, 2011 Share Posted January 14, 2011 Enclosing the variables whose values keep changing (as in a for loop) leads to quasi-intuitive results, I think. for(i = 0, len = anchors.length; i < len; i++) { something.onclick = function () { alert(i); };} You might think that i would be enclosed in the function with the value it had when the function was defined, and that this would be the case with each iteration. But it is not. Unique copies of i are not enclosed. Only one copy of i exists, and that's what gets enclosed. The value that gets alerted in this system will be the value of i when it was last referenced, and that will be true for each anonymous function. So in this case, the value of i will always be anchors.length, no matter which element is clicked.BUT! Try this experiment: function Ob () { for (var i = 0; i < 3; i++){ this[i] = function () { alert(i); i++; } }}var O = new Ob();O[0](); // alerts 3 as expectedO[1](); // alerts 4 Link to comment Share on other sites More sharing options...
jeffman Posted January 14, 2011 Share Posted January 14, 2011 Dave, are you just trying to learn about closures? I think I just explained why a closure really won't work in this situation. For the same reason that's why jsg showed you a different strategy, which is to bind the value of i to something stable.If you're goal is to learn about this thing rather than solve a problem, then I think what you've learned is which kind of problem a closure really won't solve. Link to comment Share on other sites More sharing options...
skaterdav85 Posted January 14, 2011 Author Share Posted January 14, 2011 DD, interesting approach. I guess that would work too. i was curious about closures since they keep coming up in several JS books and YUI Theatre videos and I didn't get them until now actually. So I found a pretty good explanation on Stack Overflow here and the example was pretty similar to what I was trying to do and I created something that works and what I think is a closure.new JS bin file window.addEventListener('load', function () { var anchors = document.getElementsByTagName('a'), i, len; for(i = 0, len = anchors.length; i < len; i++) { anchors[i].addEventListener('click', (function (i) { return function () { alert(i); } }(i)), false); }}, false); My example works now, and from what i've read, a closure simply put seems like a way to bind a particular instance of a variable to a function and the way it is done here and in the example on Stack Overflow is by executing a function that returns a function (Function Factory as they call it).EDIT: My definition is probably very wrong. I can see what is going on here but I don't know how I would explain what a closure is to someone haha Link to comment Share on other sites More sharing options...
jeffman Posted January 14, 2011 Share Posted January 14, 2011 I had to look at that a couple of times to see how it worked. I'm embarrassed to say I've never seen it before. Kind of clever. It is exactly what you say it is: a self-executing function returns a function with the correct value of i enclosed in it because the value has been copied into a variable that is unique to that function. (That's a mouthful.) It actually makes a little more sense if you change the identifiers a little: anchors[i].addEventListener('click', (function (newVar) { return function () { alert(newVar); }}(i)), false); This makes it clear that i itself is not being enclosed. Instead, i is being passed by value into newVar. In turn, newVar is enclosed by the innermost function, and newVar has a stable value because it really is a unique variable with each iteration.(This was just an illustration. Using the same in the different contexts keeps the intention clear.) Link to comment Share on other sites More sharing options...
thescientist Posted January 15, 2011 Share Posted January 15, 2011 DD, interesting approach. I guess that would work too. i was curious about closures since they keep coming up in several JS books and YUI Theatre videos and I didn't get them until now actually. So I found a pretty good explanation on Stack Overflow here and the example was pretty similar to what I was trying to do and I created something that works and what I think is a closure.new JS bin filewindow.addEventListener('load', function () { var anchors = document.getElementsByTagName('a'), i, len; for(i = 0, len = anchors.length; i < len; i++) { anchors[i].addEventListener('click', (function (i) { return function () { alert(i); } }(i)), false); }}, false); My example works now, and from what i've read, a closure simply put seems like a way to bind a particular instance of a variable to a function and the way it is done here and in the example on Stack Overflow is by executing a function that returns a function (Function Factory as they call it).EDIT: My definition is probably very wrong. I can see what is going on here but I don't know how I would explain what a closure is to someone haha hah, that is pretty slick. Link to comment Share on other sites More sharing options...
skaterdav85 Posted January 17, 2011 Author Share Posted January 17, 2011 it is an interesting approach by using a function's local scope to preserve an instance of i for each iteration. this was just one example, but do closures usually involve the execution of a function to returning a function with unique values bound to its function arguments? Link to comment Share on other sites More sharing options...
justsomeguy Posted January 17, 2011 Share Posted January 17, 2011 That's basically what a closure is, certain variables are kept in a scope because they're being used by a (usually anonymous) function. Link to comment Share on other sites More sharing options...
jeffman Posted January 18, 2011 Share Posted January 18, 2011 A simple closure, the kind I generally use, does not look like this example. It's more like: function obConstructor (x, y, z) { this.calc = function () { return (x + y) / z; }}var ob = new obConstructor(2, 4, 9);alert(ob.calc() ); Kind of a silly example, but you get the idea. Link to comment Share on other sites More sharing options...
thescientist Posted February 8, 2011 Share Posted February 8, 2011 i know it's an old thread but just wanted to say I had a need to do just this kind of thing involving closures and callbacks, and thank goodness I remember this. Thanks for your example in particular DD, it worked pretty nice as a model scene.setupCannedButtons = function(){ var canned = []; var basePath = "images/buttons/home/"; var utilObj = root.AVJS.TVV.utils; for(var i = 0, l = root.TVV.mosaics.length; i < l; i++){ var mosaic = root.TVV.mosaics[i]; if(mosaic.canned && mosaic.name != "Showcase"){ canned[canned.length] = { "name" : mosaic.name } }; }; for(var j = 0; j < 3; j++){ var name = canned[j].name.toLowerCase(); var pathOn = basePath + name + "_on.png"; var pathOff = basePath + name + "_off.png"; onClick = function(name){ var mosaic_name = name.toUpperCase(); this.click = function(){ scene.loadCannedMosaic(mosaic_name); }; }; var func = new onClick(name); utilObj.setupOnOffClick(scene['img_canned' + (j+1)], pathOn, pathOff, func); };}; and the utility using the callback (function($) { $.TVV.utils.setupOnOffClick = function (button, onImg, offImg, onClick) { button.loadImage(offImg); button.onFocus = function (previous) { button.loadImage(onImg); }; button.onBlur = function (next, oldIndex) { button.loadImage(offImg); }; button.onClick = onClick.click; };}(root.AVJS)); thanks! Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.