动作脚本3 - 参考与价值

时间:2010-02-17 14:15:17

标签: flash actionscript-3 reference

我以为我在AS3中找到了参考资料,但以下行为让我感到困惑:

// declarations for named individual reference later on
var myAmbientSound:Sound;
var myAmbientSoundLocation:String = "http://ambient_sound_location";

var myPeriodicSound:Sound;
var myPeriodicSoundLocation:String = "http://periodic_sound_location";

var myOccasionalSound:Sound;
var myOccasionalSoundLocation:String = "http://occasional_sound_location";

// for iterating through initialization routines
var mySoundArray:Array = [myAmbientSound, myPeriodicSound, myOccasionalSound];
var mySoundLocation:Array = [myAmbientSoundLocation, myPeriodicSoundLocation, myOccasionalSoundLocation];

// iterate through the array and initialize
for(var i:int = 0; i < mySoundArray.length; i++) {
    mySoundArray[i] = new Sound();
    mySoundArray[i].load(new URLRequest(mySoundLocation[i]));
}

此时,我认为mySoundArray[0]会引用与myAmbientSound相同的对象;但是,访问myAmbientSound会抛出空指针异常,而mySoundArray[0]会按预期工作并引用Sound对象。我在这里误解了什么?

4 个答案:

答案 0 :(得分:7)

它更像是java引用变量而不是C指针。

var myAmbientSound:Sound;
var myPeriodicSound:Sound;
var myOccasionalSound:Sound;
//these variables are not initialized and hence contain null values

现在创建一个包含这些变量的当前值(null)的数组

var mySoundArray:Array = [myAmbientSound, myPeriodicSound, myOccasionalSound];

现在,数组包含三个空值[null, null, null],而不是指向Sound个对象的三个指针。

现在,当您调用mySoundArray[0] = new Sound();时,会创建一个新的Sound对象并将其地址分配给该数组的第一个位置 - 它不会修改myAmbientSound变量。

答案 1 :(得分:3)

由于其他人花了很多时间提出一些非常好的解释,我会试着暗中解决你提出的问题:

  

此时,我认为mySoundArray[0]会将同一个对象引用为myAmbientSound

但是myAmbiendSound没有引用任何内容。从你的代码:

var myAmbientSound:Sound;

上面只是创建一个Sound类型的局部变量,一个潜在的引用。用外行人的话说,一个可以指向某种东西的“手指”。所有局部变量都是引用。顺便说一下,并非所有引用都是局部变量,对象属性也是引用,ArrayVector元素也是如此。无论如何,上面的表达式不会创建Sound类的对象。它只声明一个可能引用Sound类对象的局部变量。声明后,它具有特殊值undefined

声明上面的变量并为其赋值是有区别的。运算符new创建一个对象并返回对它的引用。赋值运算符=导致左侧引用的任何内容,右侧的任何内容,粗略地放置。

使局部变量高于引用对象的一种方法是:

myAmbientSound = new Sound();

在你的情况下,就像我说的那样,myAmbientSound变量有一个特殊的undefined值,因为你只是声明它,而且还没有赋值。另外,正如我们从代码中看到的那样,它在代码片段的整个运行时期间都没有引用任何内容。

现在,在我解释你最终用你的行放入你的数组之前:

 var mySoundArray:Array = [ myAmbientSound, ...

,你必须记住,数组元素,就像对象属性一样,也是引用 - 例如mySoundArray[0]可以引用一个对象,可能会引用mySoundArray[1]等等。

上面的代码行声明了一个新的局部变量使它引用一个新的数组对象。声明和定义在一个单一的声明中。

现在,由于我们已确定您的本地变量myAmbiendSound包含特殊的undefined值,因此您的第一个数组元素最终会引用相同的值 - undefined最初< / strong>即可。在稍后的循环中,你有第一个元素(或者,如果我们要谨慎,变量i引用的数字处的元素)引用一个新的Sound对象。此时(以及我们观察到的整个代码段范围),由于myAmbiendSoundundefined,因此将两者进行比较将失败 - 它们不会引用同一个对象,它们引用的对象也不是“相等的”(如果=在AS3中的工作方式是另一个主题) - 前者是undefined,后者指向Sound个对象。

此外,一个小修正:“访问”对象不会导致运行时异常。尝试访问指向对象的引用的属性(使用点表示法语法)会导致运行时异常。这就像你写trace(null.foo) - null不是一个对象,当然也没有名为foo的属性。同样适用于undefined

顺便说一下,null == undefinedtrue,但null === undefinedfalse。只是说。在很少发生时(当然取决于你),你必须尊重这个细节。

我希望这可以解决问题。我认为这是前面所说的所有内容的补充。

答案 2 :(得分:2)

通常,ActionScript,Java和其他语言中的引用不会被视为C / C ++中的引用。如果引用在C / C ++中起作用,那么您编写的代码将是正确的并且按预期工作。通常,您应该尝试将引用视为自动解引用指针。您的代码是C / C ++引用和现代引用之间差异的一个很好的例子。

以下是此行为的后果示例,因为它适用于垃圾收集器:

var someRef:Sound = new Sound();
someRef = null;

然后我在第一行创建的Sound对象仍然存在,并且可能继续存在。这一点很重要,因为如果在AS3中注册事件侦听器,则会为它们创建一个额外的引用,以防止它们被GC销毁。这就是为什么你应该几乎总是使用对AddEventListener的完全调用,强制它使用弱引用(不计入GC)。

此外,请记住,所有函数调用都是AS3中的ByVal。所以这段代码不起作用:

function initSound(a:Sound, b:String):void {
    a = new Sound();
    a.load(new URLRequest(b);
}

var myAmbientSound:Sound;
var myAmbientSoundLocation:String = "http://ambient_sound_location";
initSound(myAmbientSound, myAmbientSoundLocation);

好消息是,大多数内置垃圾收集的语言都具有相同的引用行为,因此一旦掌握了它,您就能更快地获取这些语言。

答案 3 :(得分:1)

定义变量不会创建指针。

// This line is only creating a QName, but no pointer.
// Value of "myAmbientSound" is "null".
var myAmbientSound : Sound;

// Here, you put the reference pointed by myAmbientSound into the array.
// It is null, so mySoundArray[0] will also be null.
var mySoundArray : Array = [myAmbientSound];

// Here, you assign a new pointer to the index 0 of mySoundArray.
// It is simply overwriting the "null" value, but there is no
// connection to myAmbientSound.
mySoundArray[0] = new Sound();

// myAmbientSound is still null, as it is still pointing to nothing.
trace(myAmbientSound);  // null
// But index 0 of mySoundArray holds a pointer to the sound you instanciated.
trace(mySoundArray[0]); // [Object Sound]

要实现您的目标,您必须做同样的事情:

// Creating the variable and pointing it to an object instance.
var myAmbientSound : Sound = new Sound();

// Here, the pointer isn't null, so the reference can be added to the array.
var mySoundArray : Array = [myAmbientSound];

// And now, both accessors points to the same object instance.
trace(myAmbientSound);  // [Object Sound]
trace(mySoundArray[0]); // [Object Sound]