对象包含相同的值但不应该

时间:2015-11-17 12:44:03

标签: javascript javascript-objects

我尝试实现对象继承,其中每个对象共享相同的功能但包含不同的值/容器。这样做我发现了一种奇怪的行为;每个单独的对象都像魅力一样,但它们都有相同的选择。请澄清我的意思,我添加了一个示例代码。

我想要做的是从每个元素中读取数据值并将其绑定到选项。在某些功能中需要这些才能正常工作!

<div class="first" data-value="first div"></div>
<div class="second" data-value="second div"></div>
<script>
    var original = {
        options: {
            value: null,
            factor: 13
        },

        init: function (container) {
            this.options.value = container.getAttribute('data-value');
        }
    };

    var variation = Object.create(original);
    variation.options.factor = 37;

    var firstDiv = Object.create(variation);
    var secondDiv = Object.create(variation);

    firstDiv.init(document.getElementsByTagName('div')[0]);
    secondDiv.init(document.getElementsByTagName('div')[1]);

    alert('firstDiv.options === secondDiv.options: ' + (firstDiv.options === secondDiv.options)); // but should be false!
</script>

请注意,这只是显示实际物体的一小部分。在我看来,所有其他部分都是无关紧要的。

我希望它清楚问题是什么。另外我故意使用Object.create()

2 个答案:

答案 0 :(得分:5)

  

对象包含相同的值,但不应该

他们应该,因为你没有更改options上的variation属性,所以它仍然指向original.options指向的同一个对象。

执行此操作时:

var original = {
    options: {
        value: null,
        factor: 13
    },

    init: function (container) {
        this.options.value = container.getAttribute('data-value');
    }
};

这是你在记忆中得到的东西(省略了一些细节):

                                  +--------------------+
            +------------+   +--->| (Object.prototype) |<-+
original--->|  (object)  |   |    +--------------------+  |
            +------------+   |                            |
            | __proto__  |---+    +-------------+         |
            | options    |------->|  (object)   |         |
            | init       |---+    +-------------+         |
            +------------+   |    | __proto__   |---------+
                             |    | value: null |
                             |    | factor: 13  |
                             |    +-------------+
                             |
                             |    +------------+
                             +----| (function) |
                                  +------------+

...其中__proto__是一个伪属性,显示对象的原型是什么(具体来说,是spec调用对象的内部[[Proto]]属性的值)。 (在浏览器上,在ES2015中,实际上有一个__proto__访问者,但不应该使用它。)

然后当你这样做时:

var variation = Object.create(original);

你有:

            +------------+
variation-->|  (object)  |
            +------------+
            | __proto__  |--+
            +------------+  |
                            |                              
         +------------------+     +--------------------+   
         |                     +->| (Object.prototype) |<-+
         \  +------------+     |  +--------------------+  |
original--->|  (object)  |     |                          |
            +------------+     |                          |
            | __proto__  |-----+  +-------------+         |
            | options    |------->|  (object)   |         |
            | init       |---+    +-------------+         |
            +------------+   |    | __proto__   |---------+
                             |    | value: null |
                             |    | factor: 13  |
                             |    +-------------+
                             |
                             |    +------------+
                             +----| (function) |
                                  +------------+

您可以看到originalvariation仍然指向相同的选项对象。

如果需要单独的选项对象,则必须创建新对象。您可以variation.options使用original.options作为原型:

var variation = Object.create(original);
variation.options = Object.create(original.options);

...但是你必须再次 firstDivsecondDiv

var firstDiv = Object.create(variation);
firstDiv.options = Object.create(variation.options);
var secondDiv = Object.create(variation);
secondDiv.options = Object.create(variation.options);

......这表明我们需要做一些不同的事情。

您可以使用以下功能执行此操作:

function createVariation(source) {
    var v = Object.create(source);
    v.options = Object.create(source.options);
    return v;
}

var variation = createVariation(original);
var firstDiv = createVariation(variation);
var secondDiv = createVariation(variation);

...但您可能会看一下构造函数或制造商函数,以及一个称为“扩展”函数的相当典型的模式(例如jQuery'sUnderscore's)。

答案 1 :(得分:1)

firstDivsecondDiv都没有自己的options属性。两者都从options继承original。您可以通过观察下面的所有表达式false

来测试这一点
variation.hasOwnProperty("options") // false
firstDiv.hasOwnProperty("options")  // false
secondDiv.hasOwnProperty("options") // false

您的init函数执行this.options.value = ...,但暂时只考虑this.options这个表达式。您永远不会创建另一个options对象,因此当您为其firstDiv对象请求secondDivoptions时,会将this.options查找升级为{{1}原型对象。

因此,当您访问originaloptions的{​​{1}}属性时,您将导致属性查找升级到firstDiv(原型中的第一个对象)真正具有secondDiv属性的链。这意味着originaloptions是完全相同的对象(即firstDiv.options)。因此,当您设置secondDiv.options时,您也设置了original.options

此处的解决方案是确保secondDiv.options.valuefirstDiv.options.value明确拥有自己的firstDiv属性。但是,您还希望secondDiv值本身继承自options,并且您希望options也拥有自己的variation.options值,该值继承自variation }。 T.J. Crowder's answer使用options方法详细说明了这一点:

original.options