对象包含相同的值但不应该
Objects contain same values but shouldn't
我尝试实现对象继承,其中每个对象共享相同的功能但包含不同的 values/containers。这样做我发现了一个奇怪的行为;每个单独的对象都像一个魅力,但它们都共享相同的选项。请澄清我的意思,我添加了示例代码。
我想做的是从每个元素读取数据值并将其绑定到选项。这些在某些函数中是必需的才能正常工作!
<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()
。
objects contain same values but shouldn't
它们应该,因为您没有更改 variation
上的 options
属性,所以它仍然指向 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__
是伪 属性 显示对象的原型是什么(具体来说,规范调用对象内部的值 [[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) |
+−−−−−−−−−−−−+
您可以看到 original
和 variation
仍然指向 相同的 选项对象。
如果您想要单独的选项对象,则必须创建一个新对象。你可以让 variation.options
使用 original.options
作为它的原型:
var variation = Object.create(original);
variation.options = Object.create(original.options);
...但是对于 firstDiv
和 secondDiv
:
,您必须再次
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's or Underscore's)。
firstDiv
和 secondDiv
都没有自己的 options
属性。两者都从 original
继承了 options
。您可以通过观察下面的所有表达式都是 false
:
来测试这一点
variation.hasOwnProperty("options") // false
firstDiv.hasOwnProperty("options") // false
secondDiv.hasOwnProperty("options") // false
您的 init
函数执行 this.options.value = ...
,但暂时只考虑表达式 this.options
。您永远不会创建另一个 options
对象,因此当您为其 options
对象请求 firstDiv
或 secondDiv
时,会将 this.options
查找升级到 original
原型对象。
因此,当您访问 firstDiv
或 secondDiv
的 options
属性 时,您会导致 属性 查找升级为 original
(原型链中第一个真正具有 options
属性 的对象)。这意味着 firstDiv.options
和 secondDiv.options
是完全相同的对象(即 original.options
)。因此,当您设置 secondDiv.options.value
时,您也在设置 firstDiv.options.value
.
此处的解决方案是确保 firstDiv
和 secondDiv
明确拥有自己的 options
属性。但是,您还希望 options
值本身继承自 variation.options
,并且您希望 variation
也有自己的 options
值,它继承自 original.options
. 用他的 createVariation
方法详细解决了这个问题:
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);
我尝试实现对象继承,其中每个对象共享相同的功能但包含不同的 values/containers。这样做我发现了一个奇怪的行为;每个单独的对象都像一个魅力,但它们都共享相同的选项。请澄清我的意思,我添加了示例代码。
我想做的是从每个元素读取数据值并将其绑定到选项。这些在某些函数中是必需的才能正常工作!
<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()
。
objects contain same values but shouldn't
它们应该,因为您没有更改 variation
上的 options
属性,所以它仍然指向 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__
是伪 属性 显示对象的原型是什么(具体来说,规范调用对象内部的值 [[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) | +−−−−−−−−−−−−+
您可以看到 original
和 variation
仍然指向 相同的 选项对象。
如果您想要单独的选项对象,则必须创建一个新对象。你可以让 variation.options
使用 original.options
作为它的原型:
var variation = Object.create(original);
variation.options = Object.create(original.options);
...但是对于 firstDiv
和 secondDiv
:
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's or Underscore's)。
firstDiv
和 secondDiv
都没有自己的 options
属性。两者都从 original
继承了 options
。您可以通过观察下面的所有表达式都是 false
:
variation.hasOwnProperty("options") // false
firstDiv.hasOwnProperty("options") // false
secondDiv.hasOwnProperty("options") // false
您的 init
函数执行 this.options.value = ...
,但暂时只考虑表达式 this.options
。您永远不会创建另一个 options
对象,因此当您为其 options
对象请求 firstDiv
或 secondDiv
时,会将 this.options
查找升级到 original
原型对象。
因此,当您访问 firstDiv
或 secondDiv
的 options
属性 时,您会导致 属性 查找升级为 original
(原型链中第一个真正具有 options
属性 的对象)。这意味着 firstDiv.options
和 secondDiv.options
是完全相同的对象(即 original.options
)。因此,当您设置 secondDiv.options.value
时,您也在设置 firstDiv.options.value
.
此处的解决方案是确保 firstDiv
和 secondDiv
明确拥有自己的 options
属性。但是,您还希望 options
值本身继承自 variation.options
,并且您希望 variation
也有自己的 options
值,它继承自 original.options
. createVariation
方法详细解决了这个问题:
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);