Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

So, I have this jQuery .each loop, and for the most part its working as intended; there is one issue, but first the loop:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
    <head>
        <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.1.min.js"></script>
        <script type="text/javascript">
            function Pushpin(){}
            Pushpin.prototype.XZX = {
                site: null,
                getHtmlDescription: function () {
                    var html  = '<b id="infoboxTitle" style="position:absolute; top:10px; left:10px; width:220px;">' + this.site.Name + '</b>';
                        html += '<a id="infoboxDescription" style="position:absolute; top:30px; left:10px; width:220px; height:120px;">{0}</a>';

                    var description = 'Headcount: ' + this.site.Headcount + '<br />';
                    description += 'Leases: ' + this.site.LeaseCount + '<br />';

                    html = html.replace('{0}', description);

                    return html;
                }
            };

            var data = [
                    {"Address":{"City":"Atlanta","Country":"USA","County":"","Latitude":33.9882404987503,"Longitude":-84.1629638209203,"Region":"Southeast","State":"GA","StreetAddress":"Atlanta 177","ZipCode":"30096"},"Headcount":0,"ImageBytes":null,"ImageRefPath":"","LeaseCount":1,"Leases":null,"Name":"Atlanta","NextExpire":"\/Date(1495083600000-0500)\/","Number":"1052","PrimaryUse":"Garage","PropertyID":"OMNI","RecID":32839,"RecordID":1004,"RentableSquareFootage":22000,"SiteRecordID":"DEMO_29626","SiteTotalDollars":0,"Status":null,"Type":"LSE"},
                    {"Address":{"City":"Bellevue","Country":"USA","County":"","Latitude":47.6043250620083,"Longitude":-122.14236047437,"Region":"Northwest","State":"WA","StreetAddress":"Seattle 51","ZipCode":"98007"},"Headcount":0,"ImageBytes":null,"ImageRefPath":"","LeaseCount":1,"Leases":null,"Name":"Bellevue","NextExpire":"\/Date(1260424800000-0600)\/","Number":"1078","PrimaryUse":"Tower","PropertyID":"OMNI","RecID":32865,"RecordID":1027,"RentableSquareFootage":7652,"SiteRecordID":"DEMO_275651","SiteTotalDollars":0,"Status":null,"Type":"LSE"}
                ]; 

            var mylist = []; 
             $.each(data, function (i, item) { 
                try {
                    var pin = new Pushpin();  
                    pin.XZX.site = item;
                    mylist.push(pin); 
                } catch (e) { alert (e); } 
             });
            $(document).ready(function() {
                $('#btnAlert').click(function () { 
                    $('#content').html(mylist[$('#index').val()].XZX.getHtmlDescription());
                } );
            });
        </script>
    </head>
    <body >
        <div style="margin-left:auto; margin-right:auto; width:300px;">
            <div style="position:relative; width:250px;">
                <select id="index">
                    <option>0</option>
                    <option>1</option>
                </select> 

                <input type="submit" id="btnAlert"/>
            </div>
            <div id="content" style="position:relative;width:250px;"></div>
        </div>
    </body>
</html>

Also available on jsfiddle: http://jsfiddle.net/M8YS2/

At the end of the loop, mylist[x].site for any x all point to the same instance of my data item, how can I get around this?

share|improve this question

4 Answers 4

up vote 4 down vote accepted

The issue is that each pin.XYZ is the same object -- namely Pushpin.prototype.XYZ.

The simple "fix" is to use:

var pin = new Pushpin(...)
pin.XYZ = {
   site: item
   // the following will get tedious fast, consider one of the "property copy"
   // implementations floating about -- including jQuery.extend   
   getHtmlDescription: Pushpin.prototype.XYZ.getHtmlDescription
}

Which will assign a new object to the XYZ property of each new Pushpin object. Of course, this could be designed differently as well :)

At the very least, move XYZ off the Pushpin.prototype object -- this will allow it to treated nicely as an object (the way that this is passed about actually makes it nigh-impossible for a function dangling off an object of a prototype to access instance data of the object to which the prototype applies); the end-code might look something like:

// We .. "wrap" the real Pushpin constructor
// somewhere global after Bing Mapi JS loaded
Pushpin = (function (bingPushpin) {
   return function Pushpin (...) {
       var pin = new bingPushpin(...)
       pin.XYZ = new XYZ()
       // trick of "returning" from ctor
       return pin
   }
})(PushPin)
// ...
var pin = new Pushpin(...)
pin.XYZ.site = item

Happy coding.


Pre-update answer:

This actually isn't a scoping issue -- there are no inadvertent closures created and each expression is strictly evaluated.

I suspect there is another problem, such as unexpected input (data contains a bunch of the same item) or flawed assumption (such that objects are magically cloned) or something unrelated.

Happy coding.


Analysis:

 var mylist = [];
 $.each(data, function (i, item) {
     // creates new object
     var pin = new Pushpin(x, y);
     // property of new object assigned
     // remember that no "duplication" is occurring
     pin.site = item;
     // new object pushed to array
     mylist.push(pin);
 });

Therefor, no pin will be the same but it is possible that item evaluates to the same object each loop. (The only exception to this is if the Pushpin constructor uses return to return an existing object, which would be a fun fine indeed.)

share|improve this answer
    
The Pushpin is from Bing Maps (not sure if it uses return or not). I have verified that item is different each time through the loop. –  Nate Jun 16 '11 at 21:02
    
@Nate Don't worry about the Pushpin ctor then -- it would break everything in terribly wonderful ways. Do you have a minimal test-case showing the behavior? (jsfiddle.net is a good place to post it, as well as in the original post). For the test-case it should be sufficient to use var pin = {} and remove the Bing Maps dependency. –  user166390 Jun 16 '11 at 21:03
    
No, I'll try to get one. Would it make a difference if my .each loop is in the callback for a $.getJSON call? –  Nate Jun 16 '11 at 21:07
    
@Nate Not inherently, but the larger context may contain more clues. –  user166390 Jun 16 '11 at 21:10
    
OK, I have a test-case that demonstrates the behavior I'm seeing: jsfiddle.net/M8YS2 –  Nate Jun 16 '11 at 21:42

Considering you're within a function I would say yes of course they all point to the same object as you're only passing by reference. After looking around I stumbled upon this - http://my.opera.com/GreyWyvern/blog/show.dml/1725165 - but it doesn't look like there is a straight-forward option for cloning a Javascript object.

Perhaps your best approach would be to write a function that clones an input object and returns it as a new instance?

share|improve this answer
    
Pedantic: Objects in JavaScript are not passed by reference (a reference is an underlying implementation technique/detail that is never exposed in the JavaScript language). Rather, JavaScript objects are passed as themselves (not a copy, clone or duplicate). This calling behavior is known as Pass-By-Object or Pass-By-Object-Sharing. –  user166390 Jun 16 '11 at 21:12

After reading MoarCodePlz's answer I thought maybe this could help getting around the 'by reference' issue. Haven't verified it though.

 var mylist = [];
 $.each(data, function (i, item) {
     // Creates the new object as a part of yourlist
     mylist.push(new Pushpin(x, y));
     // property of new object assigned item
     mylist[x].site = item;
 });
share|improve this answer
    
This won't fix anything. An assignment never creates a new object (primitive values can be excluded from the discussion for simplicity). –  user166390 Jun 16 '11 at 21:02
    
Okay. Thought it was worth a try. :) –  Dan Jun 16 '11 at 21:06
    
Don't stop trying :-) –  user166390 Jun 16 '11 at 21:12

Do you need to declare the var pin outside of the .each? then set it to new inside the .each.

var pin;
 var mylist = [];
 $.each(data, function (i, item) {
     try {
         pin = new Pushpin(x, y);
         pin.site = item;
         mylist.push(pin);
     } catch (e) { alert (e); }
 });
share|improve this answer
    
Sadly, this does not work. –  Nate Jun 16 '11 at 21:08

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.