我想通过flex 3中的函数设置arrayCollection #2 = arrayCollection #1。我将两个数组集合传递给一个函数,并设置arrayCollection #2 = arrayCollection #1。然而,它似乎没有通过引用传递arrayCollection #2,因为在函数调用之后,arrayCollection #2没有被改变。我的理解是应该通过参考和工作来传递,我是不是做错了什么?下面是代码:
var AC1:ArrayCollection = new ArrayCollection;
var AC1.addItem(someObject);
var AC2:ArrayCollection = new ArrayCollection;
setAC2(AC1,AC2);
// AC2 is not set to AC1 after the function
private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
_ac2 = _ac1;
}
请参见评估策略。
AS使用"通过对象传递"/"通过对象共享传递"。也就是说,传递"对象"(不是副本、克隆或副本),并且对对象的任何修改都是共享的。
然而,赋值_ac2 = _ac1
只改变[函数的局部]参数变量的值,在函数调用期间不会对任何变量产生影响。传入的唯一东西是值("对象"),这些值是由函数调用中使用的变量(或任何任意表达式)的求值产生的。
这是因为,如上所述,使用的策略是"按对象传递",而不是(如文档所述"按引用传递",这实际上意味着"按[引用的]值传递"或只是…"通过对象传递")。也就是说,术语"通过引用传递"实际上被误用了,因此,混淆了。(它在许多语言和文档中被误用。这是一场艰苦的战斗,试图达到共同的意义。
如果确实是"通过引用传递",那么将新值赋给_ac2
将传播出去。(在提出评论说AS是如何"通过引用传递"之前,请参阅顶部的链接,并考虑"通过引用传递"涵盖了c#的out/ref
, VB的ByRef
, TSQL的output
和c++的(参考)&
的情况-这些概念不在AS, Javascript或Java中)。然而,正如原始帖子(以及额外的自我回复)中正确指出的那样,情况并非如此——结论: as不支持"通过引用传递";此外,文档(令人困惑地)使用术语"通过引用传递"来表示"通过对象传递"/"通过对象共享传递"。
有几种方法可以将更改传播出去,按(我的)偏好排序:
-
返回新的适用值:
AC2 = doSomeTransformation(AC1)
。这通常是最干净的。避免副作用和令人惊讶的代码。如果在合适的对象(或数组)中包装,则可以返回多个值。 -
使用闭包:
doSomeTranformation(AC1, function (newValue) { AC2 = newValue })
,其中doSomeTransformation可能看起来像:function doSomeTransformation(_ac1, finished) { ...; finished(_ac1) }
。我通常只在回调"在函数本身的上下文中运行"或以cps风格编写代码时使用它。 -
改变一个对象(毕竟AS是"通过对象传递")。这是非常讨厌的,但它会工作。
var blah = {AC2: null}; doSomeTransformation(ac1, blah); ...; laterOn(blah.AC2)
的doSomeTransformation可能看起来像function doSomeTransformation(_ac1, b) { ...; b.AC2 = _ac1; }
。一般不建议使用
幸福的编码。
适用摘录,来自评估策略:
"调用引用":(我对"调用引用" 使用不正确的主要论点是它已经有一个定义良好的含义;某些语言(如as和Python)采用的重载术语只会增加混淆)
在按引用调用求值(也称为按引用传递)中,函数接收对用作参数的变量的隐式引用,而不是其值的副本。这通常意味着函数可以修改用作参数的变量——这将被调用者看到。
"调用对象"/"调用对象共享":(但要注意它承认这些术语的不一致/本地化;术语"按引用调用"经常被误用来暗示这些语义,而"按[引用的]值调用"在某些上下文中也被用来表示相同的含义)
按共享调用的语义与按引用调用的语义不同,在函数内对函数参数的赋值对调用者是不可见的(与按引用语义不同),因此,例如,如果传递了一个变量,在调用者的作用域中不可能模拟对该变量的赋值。然而,由于函数可以访问与调用者相同的对象(没有复制),如果对象是可变的,那么在函数内对这些对象的更改对调用者是可见的,这可能与按值调用语义不同。
ActionScript中的函数参数通过值传递,而不是通过引用传递。这与Java中的完全相同。
我看到的问题是
var AC1.addItem(someObject);
尝试在函数中添加项。
var AC1:ArrayCollection = new ArrayCollection;
var AC2:ArrayCollection = new ArrayCollection;
addItemToArrayCollection( AC1 );
setAC2(AC1,AC2);
// AC2 should be pointing to the ArrayCollection that AC1 is pointing to.
private function setAC2(_ac1:ArrayCollection, _ac2:ArrayCollection):void
{
_ac2 = _ac1;
}
private function addItemToArrayCollection( arrayCollection:ArrayCollection ):void
{
arrayCollection.addItem( someObject );
}
您可以在赋值后添加一个断点,并看到AC2应该具有与AC1相同的Object。
我相信@Constantiner是对的,但我认为他的解释缺乏细节;所以我会试着更深入地解释;据我所知。如果我说错了,你可以纠正我。
如文档中所述:
在ActionScript 3.0中,所有参数都是通过引用,因为所有值以对象的形式存储。然而,属于原语的对象数据类型,包括布尔值,Number、int、uint和String都有制造它们的特殊操作人员表现得好像他们是路过的价值。
所以,ArrayCollection绝对是一个对象,而不是一个基本类型,所以它应该通过引用传递,并且表现得像通过引用传递一样。但是,你对ArrayCollection的引用变量是什么。从概念上讲,它只是一个指向包含实际Collection数据的内存空间的指针。这是我的一些ASCII艺术的尝试:
|---|
ac1(variable)--> | | (ActualArrayCollection1)
|---|
|---|
ac2(variable)--> | | (ActualArrayCollection2)
|---|
重复, ac1variable是指向某个内存空间的指针。Ac2variable是指向不同内存空间的指针。当您将其中一个作为参数传递给方法时,它是通过引用传递的。在方法内部,你有这样的东西:
ac1(variable)--> |---|
ac1(argument)--> | | (ActualArrayCollection1)
|---|
ac2(variable)--> |---|
ac2(argument)--> | | (ActualArrayCollection2)
|---|
所以ac1variable和ac1argument都指向同一个内存空间;因为它们都包含相同的指针值。但是,ac1variable和ac1argument实际上占用了不同的内存空间。它们不一样。
当方法运行这一行时:
_ac2 = _ac1;
你会得到这样的东西:
ac1(variable)--> |---|
ac1(argument)--> | | (ActualArrayCollection1)
ac2(argument)--> |---|
ac2(variable)--> |---|
| | (ActualArrayCollection2)
|---|
方法执行结束时,这两个参数消失,原始指针变量保持不变。如果您想在方法中执行这样的直接赋值操作,可以使用this关键字访问全局变量。应该这样做:
this._ac2 = _ac1;
当然,如果您访问的是类级别的变量,那么这会破坏方法内部封装的目的。
我相信一个编译器设计方面的专家会把这个当成早餐,然后吐出来。我希望我的ASCII艺术是一致的跨多个浏览器/机器/操作系统等。