使用getter和setter时出现意外行为



查看此代码:

<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>

第一次将对象"Product"推入对象"Order"时,一切看起来都很好。当您为对象"Product"设置新值时,对象本身会覆盖对象"Order"以前的值。最终结果是一个重复值的数组。这正常吗?有变通办法吗?只是尝试了我所知道的一切,但没有成功。谢谢

疯狂列车已经在评论中回答了这个问题。列出的问题有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不支持私有成员。您可以通过创建闭包来模拟它们,但当您有特定于实例的私有化时(就像您的情况一样),这也有自己的问题。如果函数需要访问私有实例变量,则不能使用prototype,除非有公共访问器,否则不能克隆它,继承和重写函数将是一件痛苦的事情。

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

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

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

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/

最新更新