Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
156 views
in Technique[技术] by (71.8m points)

javascript - Objects contain same values but shouldn't

I try to achieve an object inheritance, where each object shares the same functionality but contains different values/containers. Doing so I discovered a strange behavior; each individual object works like a charm but they all share the same options. Do clarify what I mean, I added a sample code.

What I want to do is read the data-value from each element and bind it to the options. Those are required inside some functions to work correctly!

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

Please note, that this just shows a little part of the actual objects. All the other parts are, in my opinion, irrelevant.

I hope it's clear what the problem is. Also that I use Object.create() on purpose.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

objects contain same values but shouldn't

They should, because you haven't changed the options property on variation, so it still points at the same object that the original.options points to.

When you do this:

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

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

Here's what you get in memory (some details omitted):

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

...where __proto__ is a pseudo-property showing what the object's prototype is (specifically, the value of what the spec calls the object's internal [[Proto]] property). (On browsers, in ES2015, there is actually a __proto__ accessor, but it shouldn't be used.)

Then when you do this:

var variation = Object.create(original);

You have:

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

You can see how both original and variation are still pointing at the same options object.

If you want separate options objects, you have to create a new object. You could make variation.options use original.options as its prototype:

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

...but then you'd have to do it again for firstDiv and secondDiv:

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

...which suggests we need to do something different.

You could have a function you use to do it:

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

...but you might look at constructor functions or maker functions, and a fairly typical pattern called the "extend" function (such as jQuery's or Underscore's).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...