as3:如何按值复制对象

发布于 2024-10-15 06:50:04 字数 782 浏览 3 评论 0原文

我需要在我拥有的所有其他对象中拥有一个公共对象的实例。我正在对我拥有的每个子对象中的该对象的值进行修改。

例如。我有一张图块地图,以及一个按特定顺序在图块上移动的机器人。每个机器人都在标记他已经访问过的图块,如visited=true。但总的来说,我不希望更改主地图...

我尝试设置一个示例:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    applicationComplete="complete()">
    <mx:Script>
        <![CDATA[
            private var array:Array = new Array( 1, 2, 3, 4);
            public function complete():void
            {
                trace("here " + array);
                var a:Array = array;
                a[0] = 100;
                trace("here " + array);
            }
        ]]>
    </mx:Script>
</mx:Application>

有人可以帮助我理解如何按值复制例如数组(而不是按引用)

I need to have an instance of one common object in every other object, I have. I am doing modifications on the values of this object in every sub object I have.

For example. I have a map of tiles, and a bot moving over them in specific order. Each bot is marking tiles, which was already visited by him, as visited=true. But in general I don't want main map to be changed...

I've tried to set up an example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    applicationComplete="complete()">
    <mx:Script>
        <![CDATA[
            private var array:Array = new Array( 1, 2, 3, 4);
            public function complete():void
            {
                trace("here " + array);
                var a:Array = array;
                a[0] = 100;
                trace("here " + array);
            }
        ]]>
    </mx:Script>
</mx:Application>

Can somebody help me to understand how do I copy for example the array, by value, (not by reference)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

疏忽 2024-10-22 06:50:04

对于克隆数组,您可以使用 Array.slice

var arrCopy:Array = arrOrig.slice();

For cloning arrays you can use Array.slice.

var arrCopy:Array = arrOrig.slice();
晌融 2024-10-22 06:50:04
function clone ( arr:Array ):Array
{
    return arr.map( function( item:*, ..r ):*
        {
            return item;
        } );
}

编辑:

可能包含一些语法错误...

public class MyObject
{
    private var arr:Array;
    private var bool:Boolean;

    // ...

    public function clone ():MyObject
    {
        var obj:MyObject = new MyObject();

        // clone values
        obj.arr = this.arr.slice();
        obj.bool = this.bool;

        return obj;
    }
}
function clone ( arr:Array ):Array
{
    return arr.map( function( item:*, ..r ):*
        {
            return item;
        } );
}

edit:

Might contain some syntax errors...

public class MyObject
{
    private var arr:Array;
    private var bool:Boolean;

    // ...

    public function clone ():MyObject
    {
        var obj:MyObject = new MyObject();

        // clone values
        obj.arr = this.arr.slice();
        obj.bool = this.bool;

        return obj;
    }
}
旧时模样 2024-10-22 06:50:04

这是 poke 描述的方法的替代方法:

我首先想就 poke 的帖子提出一些观点。

  1. “为该特定对象定义您自己的克隆函数。没有任何特殊函数可以自动为任何任意对象执行此操作。”错误的。 ActionScript 有一种内置的序列化方法,称为 AMF(ActionScript 消息格式)。 AMF 可用于执行非原始对象的复制。

  2. “使用自定义克隆方法有一个很大的优点,您可以准确地决定将复制什么以及如何复制......”这正是您在序列化对象时所做的事情,因此没有与序列化相比,自定义克隆方法有很大的优势。

  3. “...并且返回类型是正确的(即您不需要强制转换)。”您也不需要强制转换序列化对象。但序列化还有一个额外的好处,那就是通用,使复制函数动态化,并且不限于特定类型。

  4. “[实现一个接口](这是非常不合适的,因为克隆返回一个特殊类型的对象)”必须定义一个返回类型使得进程静态,这将您锁定在使用特定类型。如果我们使用语言的动态属性,我们可以创建通用的克隆方法而不关心类型。这没有什么不合适的。

  5. “如果您对序列化对象进行反序列化两次,您将获得对象的副本,这只是序列化的附加效果。”通过调用不带任何参数的 slice() 或 concat() 来获取数组的副本这一事实只是这些方法的副作用。我真的不明白你的意思。此外,连载的核心是复制。序列化然后反序列化的行为就是制作副本的行为。您不会以某种方式获得完全相同的内存,并且引用和所有内容都完好无损。

我有一个问题:您将如何处理克隆方法中的嵌套非原始类型?

在你的问题中,你说“有人可以帮助我理解如何通过值复制数组(而不是通过引用)”我认为,在复制对象时,了解 shallow< 之间的区别很重要/i> 和深层 副本。

浅拷贝

这里提供的解决方案(Array.slice() 和 Array.concat())被称为拷贝。您得到的是数组的副本。如果数组的内容是原始类型(通过值传递,而不是通过引用传递的类型),那么您将拥有两个唯一的对象:原始对象和副本。但是,如果数组包含通过引用传递的对象,那么数组的原始数组和副本将具有完全相同的内容。如果对原始数组中的对象进行更改,则更改将反映在复制的数组中。虽然有时这可能是您想要的,但并非总是如此。

深层复制

深层复制将遍历您想要复制的对象的层次结构,并复制它找到的任何对象。然后,您将可以对复制的对象进行任何更改,而任何更改不会反映在原始对象中。

如果您要按照 poke 的建议定义自定义克隆方法,那么复制非原始类型就会变得过于复杂。您必须检查对象的属性并对任何非基本类型调用自定义的clone() 方法。但是,如果您遇到内置的非原始类型(如数组或字典),则必须重新创建该对象,循环遍历其内容,然后通过检查它是否为非原始类型、调用其clone()来重新开始。方法(如果有的话),或者处理数组和字典。它变得过于复杂。总而言之,这种方法有两个问题:您必须自己处理数组和字典(以及任何内置的非原始类型);您必须专门调用嵌套对象的克隆方法(并且知道它们已定义克隆方法)。

另一种方法是使用 AMF 序列化对象,然后反序列化对象,从而获得深层副本。这对于数组、字典和任何依赖公共属性的非基元来说都是开箱即用的。

var t:Array = [];
t[0] = [1, 2, 3];
t[1] = new Dictionary();
t[1]['hello'] = 'world';
t[2] = {'my': 'object'}
trace(t, t[1]['hello'], t[2]['my']); // [trace] 1,2,3,[object Dictionary],[object Object] world object
var t2:Array = clone(t);
trace(t2, t2[1]['hello'], t2[2]['my']); // [trace] 1,2,3,[object Dictionary],[object Object] world object
t[0] = [4, 5, 6];
t[1]['hello'] = 'earth';
t[2]['my'] = 'other object';
trace('modified values');  // [trace] modified values
trace(t, t[1]['hello'], t[2]['my']);  // [trace] 4,5,6,[object Dictionary],[object Object] earth other object
trace(t2, t2[1]['hello'], t2[2]['my']);  // [trace] 1,2,3,[object Dictionary],[object Object] world object

function clone(source:*):* {
    var b:ByteArray = new ByteArray();
    b.writeObject(source);
    b.position = 0;
    return(b.readObject());
}

这涵盖了自定义克隆方法的第一个问题和上面的第一点。正如您所看到的,所有对象及其内容都已使用内置方法复制。

我已在此处展示了如何创建克隆方法的实现,但您可以在以下位置找到一个实现:mx.utils.ObjectUtil。

如果你想深度复制一个私有存储数据的对象,那么你必须实现 IExternalized 接口。这将迫使您实现两种方法:

public function writeExternal(output:IDataOutput):void
public function readExternal(input:IDataInput):void

在这些函数中,您将私有变量写入输出对象,然后从输入中将它们读取到私有变量。然后,当您调用克隆时,您将获得对象的完整副本。请记住对所有嵌套对象执行此操作。

这是一个包含两个类的简单实现示例:

package {

    import flash.utils.IExternalizable;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.net.registerClassAlias;

    public class Car implements IExternalizable {

    private var type:String;
        private var contents:Array;

        public function Car() {
            registerClassAlias("Car", Car);
        }

    public function setVars(type:String, contents:Array):void {
            this.type = type;
            this.contents = contents;
        }

    public function setType(type:String):void {
            this.type = type;
        }


        public function writeExternal(output:IDataOutput):void {
            output.writeUTF(type);
            output.writeObject(contents);
        }

    public function readExternal(input:IDataInput):void {
            type = input.readUTF();
            contents = input.readObject();
    }

        public function toString():String {
            return "[Car type = " + type + ", contents = " + contents + "]";
    }

    }

}

以及:

package {

    import flash.utils.IExternalizable;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.net.registerClassAlias;

    public class Person implements IExternalizable {

    private var firstName:String;
        private var secondName:String;

        public function Person() {
            registerClassAlias("Person", Person);
        }

    public function setVars(firstName:String, secondName:String):void {
            this.firstName = firstName;
            this.secondName = secondName;
        }

        public function writeExternal(output:IDataOutput):void {
            output.writeUTF(firstName);
            output.writeUTF(secondName);
        }

    public function readExternal(input:IDataInput):void {
            firstName = input.readUTF();
            secondName = input.readUTF();
        }

        public function toString():String {
            return "[Person firstName = " + firstName + ", secondName = " + secondName + "]";
        }

    }

}

测试它们:

package {

    import flash.display.Sprite;
    import flash.utils.ByteArray;
    import flash.utils.Dictionary;

    public class Serial extends Sprite {

    public function Serial() {

            var person0:Person = new Person();
            person0.setVars("John", "Doe");
            var person1:Person = new Person();
            person1.setVars("Jane", "Doe");
            var car0:Car = new Car();
            car0.setVars("Ford", [person0, person1]);

            var person2:Person = new Person();
            person2.setVars("Joe", "Bloggs");
            var car1:Car = new Car();
            car1.setVars("Vauxhall", [person2]);

            var street:Array = [car0, car1];
            trace("street = " + street); // [trace] street = [Car type = Ford, contents = [Person firstName = John, secondName = Doe],[Person firstName = Jane, secondName = Doe]],[Car type = Vauxhall, contents = [Person firstName = Joe, secondName = Bloggs]]

            var street2:Array = clone(street);
            trace("street2 = " + street2); // [trace] street2 = [Car type = Ford, contents = [Person firstName = John, secondName = Doe],[Person firstName = Jane, secondName = Doe]],[Car type = Vauxhall, contents = [Person firstName = Joe, secondName = Bloggs]]

            person0.setVars("Max", "Headroom");
            person1.setVars("Simon", "Le Bon");
            car0.setType("Mini");

            person2.setVars("Harry", "Wotsit");
            car1.setType("Austin");

        trace("modified values of street"); // [trace] modified values of street
            trace("street = " + street); // [trace] street = [Car type = Mini, contents = [Person firstName = Max, secondName = Headroom],[Person firstName = Simon, secondName = Le Bon]],[Car type = Austin, contents = [Person firstName = Harry, secondName = Wotsit]]
            trace("street2 = " + street2); // [trace] street2 = [Car type = Ford, contents = [Person firstName = John, secondName = Doe],[Person firstName = Jane, secondName = Doe]],[Car type = Vauxhall, contents = [Person firstName = Joe, secondName = Bloggs]]

        }

        private function clone(source:*):* {
            var b:ByteArray = new ByteArray();
            b.writeObject(source);
            b.position = 0;
            return(b.readObject());
    }

    }

}

这涵盖了自定义克隆方法的第二个问题。正如您所看到的,我们不必担心调用任何克隆方法,这一切都已为我们处理好。

我并不是说它并非完全没有缺点,但它确实提供了一些深度复制对象的功能。

一些缺点包括:

  1. 构造函数中不能有任何必需的参数。
  2. 它不存储引用数据,因此如果两个对象包含相同的引用,您将得到两个不同的对象。
  3. 在哪里放置克隆方法。我认为它确实属于 Object,也许属于原型。这样做的另一个好处是使每个对象都可复制,并且如果您愿意,您可以准确指定对象的复制方式。

请参阅 Adob​​e 关于复制数组的观点:http://livedocs.adobe.com /flex/3/html/help.html?content=10_Lists_of_data_6.html

另请注意,Adobe 从 Java 窃取了这项技术。

Here is an alternative to the method described by poke:

I would first like to make some points about poke's post.

  1. "Define your own clone function for that particular object. There is no special function that does so automatically for any arbitrary object." False. ActionScript has a built in method of serialisation called AMF (ActionScript Message Format). AMF can be used to perform a copy of non-primitive objects.

  2. "Using a custom clone method has the big advantage, that you can exactly decide what will be copied, and how it will be copied..." This is exactly what you do when you serialise an object, so there is no big advantage of your custom clone method over that of serialisation.

  3. "...and that the return type is correct (i.e. you don't need to cast)." You don't need to cast a serialised object either. But serialisation has the added benefit of being generic, making the function for copying dynamic, and not limited to specific types.

  4. "[implementing an interface] (which would be highly inappropriate, given that clone returns an object of a special type)" Having to define a return type makes the process static which locks you in to using specific types. If we use the dynamic attributes of the language, we can make the a generic clone method and not care about type. There is nothing inappropriate about that.

  5. "That you get a copy of an object if you unserialize a serialized object twice, is just an additional effect of serialization." The fact you get a copy of an array by calling slice() or concat() without any arguments is just a side-effect of those methods. I don't really see your point here. Also, serialisation has copying at its heart. The act of serialising then de-serialising is the act of making a copy. You don't somehow get the exact same memory back, with references and all intact.

And I have one question: How would you deal with nested non-primitive types in your clone method?

In your question, you state "Can somebody help me to understand how do I copy for example the array, by value, (not by reference)" I think, when copying objects, it's important to know the difference between shallow and deep copies.

Shallow Copies

The solutions provided here (Array.slice(), and Array.concat()) are known as shallow copies. What you get is a copy of the array. If the contents of the array are primitive types (those that are passed by value, not by reference) then you have two unique objects, the original and the copy. However, if you're array contains objects that are passed by reference then you both the original and the copy of the array will have the exact same contents. If you make changes to an object in the original array, the changes will be reflected in the copied array. While this may sometimes what you desire, it isn't always.

Deep Copies

A deep copy will traverse the hierarchy of the object you wish to copy and make copies of any object it finds. You will then be allowed to make any changes to the copied object without any changes being reflected in the original.

If you are to define a custom clone method as poke suggests, then copying non-prinitive types becomes overly complicated. You would have to go through the object's properties and call a custom clone() method on any non-primitive type. However, if you encountered a built-in non-primitive type like Array or Dictionary, then you would have to recreate the object, loop through its contents, and start all over again by checking if it's non-primitive, calling its clone() method if it has one, or dealing with Arrays and Dictionaries. It gets overly-complicated. To summarise, this method has two problems: you have to deal with Arrays and Dictionaries (and any built-in non-primitive type) yourself; you have to specifically call the clone method on nested objects (and know that they have the clone method defined).

Another method is to use AMF to serialise and then de-serialise the object, giving you a deep copy. This works straight out-the-box for Arrays, Dictionaries, and any non-primitive that relies on public properties.

var t:Array = [];
t[0] = [1, 2, 3];
t[1] = new Dictionary();
t[1]['hello'] = 'world';
t[2] = {'my': 'object'}
trace(t, t[1]['hello'], t[2]['my']); // [trace] 1,2,3,[object Dictionary],[object Object] world object
var t2:Array = clone(t);
trace(t2, t2[1]['hello'], t2[2]['my']); // [trace] 1,2,3,[object Dictionary],[object Object] world object
t[0] = [4, 5, 6];
t[1]['hello'] = 'earth';
t[2]['my'] = 'other object';
trace('modified values');  // [trace] modified values
trace(t, t[1]['hello'], t[2]['my']);  // [trace] 4,5,6,[object Dictionary],[object Object] earth other object
trace(t2, t2[1]['hello'], t2[2]['my']);  // [trace] 1,2,3,[object Dictionary],[object Object] world object

function clone(source:*):* {
    var b:ByteArray = new ByteArray();
    b.writeObject(source);
    b.position = 0;
    return(b.readObject());
}

This covers the first problem with a custom clone method and point one above. As you can see, all objects and their contents have been copied using built-in methods.

I've shown an implementation of how to create the clone method here, but you can find one in: mx.utils.ObjectUtil.

If you want to deep copy an object that stores its data privately, then you have to implement the IExternalizable interface. That will force you to implement two methods:

public function writeExternal(output:IDataOutput):void
public function readExternal(input:IDataInput):void

Within these functions you write your private variables to the output object, then read them from the input to your private variables. Then when you call clone you will get a complete copy of your object. Remember to do this for all nested objects.

Here is a simple implementation example with two classes:

package {

    import flash.utils.IExternalizable;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.net.registerClassAlias;

    public class Car implements IExternalizable {

    private var type:String;
        private var contents:Array;

        public function Car() {
            registerClassAlias("Car", Car);
        }

    public function setVars(type:String, contents:Array):void {
            this.type = type;
            this.contents = contents;
        }

    public function setType(type:String):void {
            this.type = type;
        }


        public function writeExternal(output:IDataOutput):void {
            output.writeUTF(type);
            output.writeObject(contents);
        }

    public function readExternal(input:IDataInput):void {
            type = input.readUTF();
            contents = input.readObject();
    }

        public function toString():String {
            return "[Car type = " + type + ", contents = " + contents + "]";
    }

    }

}

And:

package {

    import flash.utils.IExternalizable;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.net.registerClassAlias;

    public class Person implements IExternalizable {

    private var firstName:String;
        private var secondName:String;

        public function Person() {
            registerClassAlias("Person", Person);
        }

    public function setVars(firstName:String, secondName:String):void {
            this.firstName = firstName;
            this.secondName = secondName;
        }

        public function writeExternal(output:IDataOutput):void {
            output.writeUTF(firstName);
            output.writeUTF(secondName);
        }

    public function readExternal(input:IDataInput):void {
            firstName = input.readUTF();
            secondName = input.readUTF();
        }

        public function toString():String {
            return "[Person firstName = " + firstName + ", secondName = " + secondName + "]";
        }

    }

}

To test them:

package {

    import flash.display.Sprite;
    import flash.utils.ByteArray;
    import flash.utils.Dictionary;

    public class Serial extends Sprite {

    public function Serial() {

            var person0:Person = new Person();
            person0.setVars("John", "Doe");
            var person1:Person = new Person();
            person1.setVars("Jane", "Doe");
            var car0:Car = new Car();
            car0.setVars("Ford", [person0, person1]);

            var person2:Person = new Person();
            person2.setVars("Joe", "Bloggs");
            var car1:Car = new Car();
            car1.setVars("Vauxhall", [person2]);

            var street:Array = [car0, car1];
            trace("street = " + street); // [trace] street = [Car type = Ford, contents = [Person firstName = John, secondName = Doe],[Person firstName = Jane, secondName = Doe]],[Car type = Vauxhall, contents = [Person firstName = Joe, secondName = Bloggs]]

            var street2:Array = clone(street);
            trace("street2 = " + street2); // [trace] street2 = [Car type = Ford, contents = [Person firstName = John, secondName = Doe],[Person firstName = Jane, secondName = Doe]],[Car type = Vauxhall, contents = [Person firstName = Joe, secondName = Bloggs]]

            person0.setVars("Max", "Headroom");
            person1.setVars("Simon", "Le Bon");
            car0.setType("Mini");

            person2.setVars("Harry", "Wotsit");
            car1.setType("Austin");

        trace("modified values of street"); // [trace] modified values of street
            trace("street = " + street); // [trace] street = [Car type = Mini, contents = [Person firstName = Max, secondName = Headroom],[Person firstName = Simon, secondName = Le Bon]],[Car type = Austin, contents = [Person firstName = Harry, secondName = Wotsit]]
            trace("street2 = " + street2); // [trace] street2 = [Car type = Ford, contents = [Person firstName = John, secondName = Doe],[Person firstName = Jane, secondName = Doe]],[Car type = Vauxhall, contents = [Person firstName = Joe, secondName = Bloggs]]

        }

        private function clone(source:*):* {
            var b:ByteArray = new ByteArray();
            b.writeObject(source);
            b.position = 0;
            return(b.readObject());
    }

    }

}

This covers the second problem with a custom clone method. As you can see, we haven't had to worry about calling any clone methods, this has all been taken care for us.

I'm not saying it isn't completely without drawbacks, but it does provide some functionality to deep copy objects.

Some drawbacks include:

  1. You cannot have any required parameters in the constructor.
  2. It doesn't store reference data, so if two objects contain the same reference, you will get two different objects back.
  3. Where to put the clone method. I think it really belongs on Object, on the prototype maybe. This would have the added benefit of making every object copyable, and if you want you can specify exactly how your object is copied.

See Adobe's point of view on copying arrays: http://livedocs.adobe.com/flex/3/html/help.html?content=10_Lists_of_data_6.html

Also note that Adobe stole this technique from Java.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文