【问题标题】:Flash AS3 function trace same valueFlash AS3 函数跟踪相同的值
【发布时间】:2012-12-24 21:20:04
【问题描述】:

有人可以解释为什么下面的跟踪返回数组的长度而不是数组项中“i”的值吗?

非常感谢,尼克

AS3

function createMarkers(mapLocations){
    var markerArray:Array = new Array();
    for(i=0; i<mapLocations.length; i++){
        markerArray.push(new marker());
        markerArray[i].x=mapLocations[i][1];
        markerArray[i].y=mapLocations[i][2];

        markerArray[i].markerText.text = mapLocations[i][0].toString();
        markerArray[i].addEventListener(MouseEvent.CLICK, function(e:MouseEvent){clickTarget(e,i);});
        bgImage.addChild(markerArray[i]);
    }
}

function clickTarget(e:MouseEvent,a){
    trace(a);
}

【问题讨论】:

  • 之前定义的是i,还是应该是for (var i:Number = 0 ... )

标签: actionscript-3 flash


【解决方案1】:

这是处理 JavaScript/ActionScript 函数时的常见错误。您遇到了问题,因为函数是 闭包,这意味着它们在定义函数时持有对范围内定义的变量的引用。

这意味着您的匿名处理函数关闭 i 变量,但它存储的是对它的引用,而不是它的值。由于i 发生变化,每个函数都持有对同一个变量的引用,该变量只会保存分配给它的最后一个值。

基本上,如果你想关闭一个变量的特定值,你必须在函数的范围内声明该变量(使用var 语句)。因此,这看起来应该可以工作:

for (var i:int = 0; i < 10; i++) {
    var scopedI:int = i;
    mc[i].addEventListener(MouseEvent.CLICK, function (e:MouseEvent) { trace(scopedI); });
}

我们在循环的范围内声明了一个新变量scopedI,以专门关闭该值,因为该变量将在循环的每次迭代中重新声明为唯一值。不幸的是,ActionScript 和 JavaScript 一样,没有有块级作用域,只有函数级作用域,所以所有的变量声明都被“提升”到了函数的顶部。

这几乎只是意味着您的scopedI 类型与i 具有相同的范围,以及在该函数中声明的任何其他变量。那么,我们怎样才能创建一个新的范围呢?具有更多功能。请记住,在 ActionScript 中,函数是对象,所以我们可以做如下疯狂的事情:

(function (id) {
    return function () { trace(id); };
})(7);

这段代码创建了一个函数,然后立即使用id 参数的值7 执行它。这很有用,因为现在id 的作用域是我们返回的内部函数,因此无论外部发生什么,该函数都会始终打印“7”。

同样,我们可以使用它来限定循环中的i 变量。您的代码可以更新为如下所示:

function createMarkers(mapLocations){
    var markerArray:Array = new Array();
    for(var i = 0; i < mapLocations.length; i++){
        markerArray.push(new marker());
        markerArray[i].x = mapLocations[i][1];
        markerArray[i].y = mapLocations[i][2];

        markerArray[i].markerText.text = mapLocations[i][0].toString();
        markerArray[i].addEventListener(MouseEvent.CLICK, (function (scopedI) {
                return function (e:MouseEvent) { clickTarget(e, scopedI); };
            })(i));
        bgImage.addChild(markerArray[i]);
    }
}

function clickTarget(e:MouseEvent, a){
    trace(a);
}

现在,scopedI 对于每次迭代都是唯一的。是的,语法有点混乱,但这最终成为语言的一个非常强大和富有表现力的特性。如果你能理解它,它会非常有用。

【讨论】:

    【解决方案2】:

    加上亚历克西斯所说的,

    由于您要添加点击事件,我假设该项目是一个显示对象。

    与 Javascript 不同,您还可以命名项目并在处理程序中检索它,例如:

            ...
            markerArray[i].name = "marker" + i; 
            markerArray[i].addEventListener(MouseEvent.CLICK, clickTarget);
            bgImage.addChild(markerArray[i]);
        }
    }
    
    function clickTarget(e:MouseEvent){
        var a = e.currentTarget.name.substr(6);
        trace(a);
    }
    

    当然,也假设 name 属性对你来说直到现在都没用。

    【讨论】:

      【解决方案3】:

      因为您的内联鼠标事件正在捕获变量 i(一个引用)而不是它的值。

      试试这个:

      function createMarkers(mapLocations){
          var markerArray:Array = new Array();
          for(var i:Number = 0; i<mapLocations.length; i++){
              var thisI:Number = i;
              markerArray.push(new marker());
              markerArray[thisI].x=mapLocations[thisI][1];
              markerArray[thisI].y=mapLocations[thisI][2];
      
              markerArray[thisI].markerText.text = mapLocations[thisI][0].toString();
              markerArray[thisI].addEventListener(MouseEvent.CLICK,
                  function(e:MouseEvent){
                      clickTarget(e,thisI);
                  });
              bgImage.addChild(markerArray[thisI]);
          }
      }
      

      这应该可以工作,因为循环的每次迭代都会重新创建 thisI

      【讨论】:

      • 我不认为这会因为提升而起作用,除非 AS3 与 JavaScript 的不同之处在于我不知道。
      猜你喜欢
      • 2018-06-23
      • 1970-01-01
      • 2011-07-31
      • 2015-08-22
      • 2010-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多