0

看这段代码:

<script>
    function dbg (object) {
        var _string = "";

        for (var a in object) {
            _string += a + ":\n";

            for (var b in object[a])
                if (/^get_/.test (b))
                    _string += "\t" + b + " - " + object[a][b] () + "\n";
        }

        return _string;
    }

    function Order () {
        var products = [];

        this.get_products = function () {return products;}

        this.set_products = function (_products) {products = _products;}
    }

    function Product () {
        var id = null;
        var name = null;

        this.get_id = function () {return id;}
        this.get_name = function () {return name;}

        this.set_id = function (_id) {id = _id;}
        this.set_name = function (_name) {name = _name}
    }

    var order = new Order ();
    var product = new Product ();

    product.set_id (1);
    product.set_name ("Banana");

    order.set_products (order.get_products ().concat (product));

    alert (dbg (order.get_products ())); // Ok

    product.set_id (2);
    product.set_name ("Orange");

    order.set_products (order.get_products ().concat (product));

    alert (dbg (order.get_products ())); // Duplicated values! What?
</script>

第一次将对象“产品”推入对象“订单”时,一切看起来都很好。当您为对象“产品”设置新值时,对象本身会覆盖对象“订单”的先前值。最终结果是一个重复值的数组。正常吗?有解决方法吗?只是尝试了我所知道的一切,但没有成功。谢谢。

4

2 回答 2

1

Crazy Train 已经在评论中回答了这个问题。列出的问题有 0 个答案,因此我将其添加为答案。

将包含对象的变量添加到数组时,您会添加对变量的引用,当您重新分配变量时,引用会被破坏。

将包含对象的变量添加到数组然后重新分配变量不会更改数组中的对象:

var arr=[];
var object={name:"John"};
arr.push(object);
object=33;
console.log(arr);//=[Object {name="john"}]

将包含对象的变量添加到数组中,然后更改该变量包含的对象的内部值确实会更改数组中的对象:

var arr=[];
var object={name:"John"};
arr.push(object);
object.name="Jane";
console.log(arr);//=[Object {name="Jane"}]

因此,要更正您的代码,您可以执行以下操作:

为要添加的产品创建一个新变量:

var product2=new Product();
product2.set_id (2);
product2.set_name ("Orange");
order.set_products (order.get_products ().concat (product2));

或者按顺序破坏您的产品变量和产品数组之间的引用:

product=null;//product has no ref to order.products
product=new Product();
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));

我不会在构造函数中定义对象的成员,var因为 JavaScript 不支持私有成员。您可以通过创建闭包来模拟它们,但是当您拥有特定于实例的私有(如您的情况)时,这有它自己的问题。如果函数需要访问私有实例变量,则不能使用原型,除非您有公共访问器,否则无法克隆它,继承和覆盖函数将很痛苦。

是有关使用构造函数的更多信息。

如果您有 Chrome 或 Firefox(带有 Firebug),那么您可以按 F12 打开控制台。您分离控制台窗口(拥有它自己的窗口)然后复制前面提到的答案中的代码并将它们粘贴到控制台的命令行中。在那里您可以运行并重新运行代码,更改并查看输出以更好地理解 JS 行为。

于 2013-08-25T01:32:10.607 回答
1

您只是覆盖对象中的变量。我会这样做,更简单:

var products = {
    set : function(name,id) {
            products.list.push({name:name,id:id});
    },
    get : function(id) {
        var r;
            if(typeof id === 'number'){
                products.list.forEach(function(e,i){ if(e.id==id) r= products.list[i];});
            } else {
                products.list.forEach(function(e,i){ if(e.name==id) r = products.list[i];});

            }
                return r;
    },
    list : []
};

var order={
    set : function(p) {
            order.list[p.id]=p;
    },
    get : function(id) {
            return order.list[id];
    },    
    delete : function(id) {
            return delete order.list[id];
    },
    list : {}
};

那么你可以这样做

products.set('apple',34);
products.set('orange',4);
products.set('mango',1);

var x = products.get(1);
var y = products.get('orange');

order.set(x);
order.set(y);

工作演示:http: //jsfiddle.net/techsin/tjDVv/2/

于 2013-08-25T03:27:07.900 回答