使用getter和setter的意外行为

时间:2013-08-24 23:32:48

标签: javascript setter getter overwrite

看一下这段代码:

<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”的先前值。最终结果是一系列重复值。这是正常的吗?有解决方法吗?刚尝试了我所知道的一切都没有成功感谢。

2 个答案:

答案 0 :(得分: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));

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

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

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

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

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

答案 1 :(得分: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/