【问题标题】:JavaScript Loop issue with variable scope变量范围的 JavaScript 循环问题
【发布时间】:2011-09-16 17:31:18
【问题描述】:

所以,我有这个 jQuery .each 循环,并且在大多数情况下它按预期工作;有一个问题,但首先是循环:

<!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>

也可以在 jsfiddle 上找到:http://jsfiddle.net/M8YS2/

在循环结束时,任何xmylist[x].site 都指向我的数据项的同一个实例,我该如何解决这个问题?

【问题讨论】:

    标签: javascript jquery variables scope each


    【解决方案1】:

    问题在于每个pin.XYZ 都是相同的 对象——即Pushpin.prototype.XYZ

    简单的“修复”是使用:

    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
    }
    

    这会将 new 对象分配给每个 new Pushpin 对象的 XYZ 属性。当然,这也可以有不同的设计:)

    至少,将 XYZ 移出 Pushpin.prototype 对象——这将允许它作为一个对象被很好地对待(this 被传递的方式实际上使得它几乎不可能让函数悬空关闭原型对象以访问原型应用到的对象的实例数据);结束代码可能类似于:

    // 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
    

    编码愉快。


    更新前的回答:

    这实际上不是范围界定问题——没有创建无意的闭包,并且每个表达式都经过严格评估。

    我怀疑还有其他问题,例如意外输入(数据包含一堆相同的项目)或有缺陷的假设(例如对象被神奇地克隆)或不相关的东西。

    编码愉快。


    分析:

     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);
     });
    

    因此,pin 不会是相同的,但item 可能在每个循环中计算相同的对象。 (唯一的例外是 Pushpin 构造函数使用 return 返回现有对象,这确实很有趣。)

    【讨论】:

    • 图钉来自必应地图(不确定它是否使用return)。我已经验证item 每次循环都是不同的。
    • @Nate 不要担心 Pushpin ctor——它会以非常美妙的方式破坏一切。您是否有显示该行为的 minimal 测试用例? (jsfiddle.net 是发布它的好地方,以及在原始帖子中)。对于测试用例,使用 var pin = {} 并删除 Bing Maps 依赖项就足够了。
    • 不,我会试着弄一个。如果我的.each 循环在$.getJSON 调用的回调中会有所不同吗?
    • @Nate 不是天生的,但更大的上下文可能包含更多的线索。
    • 好的,我有一个测试用例来演示我看到的行为:jsfiddle.net/M8YS2
    【解决方案2】:

    考虑到你在一个函数中,我会说是的,当然它们都指向同一个对象,因为你只是通过引用传递。环顾四周后,我偶然发现了这个 - http://my.opera.com/GreyWyvern/blog/show.dml/1725165 - 但它看起来不像是克隆 Javascript 对象的直接选项。

    也许您最好的方法是编写一个克隆输入对象并将其作为新实例返回的函数?

    【讨论】:

    • 迂腐:JavaScript 中的对象不是通过引用传递的(引用是一种底层实现技术/细节,它从未在 JavaScript 语言中公开)。相反,JavaScript 对象作为它们自己(不是副本、克隆或复制)传递。这种调用行为称为Pass-By-Object or Pass-By-Object-Sharing
    【解决方案3】:

    阅读 MoarCodePlz 的回答后,我认为这可能有助于解决“通过引用”问题。不过还没有验证。

     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;
     });
    

    【讨论】:

    • 这不会解决任何问题。赋值从不创建新对象(为简单起见,可以将原始值排除在讨论之外)。
    • 好的。觉得值得一试。 :)
    【解决方案4】:

    您需要在 .each 之外声明 var 引脚吗? 然后在 .each 中将其设置为 new。

    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); }
     });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-30
      • 2023-03-04
      • 2012-02-03
      • 2011-04-13
      • 1970-01-01
      • 2016-01-14
      • 2012-06-12
      相关资源
      最近更新 更多