我有以下嵌套数据结构:
{ x: { y: 'foo', z: 'bar' } }
此数据存储在原型对象上,该对象是具体对象和其他原型的父对象。
我想在不影响父原型的情况下分配继承对象的x.y
属性。此外,我希望将x.z
的访问权委托给原型。
这样做的最佳方式是什么?
这是一个可执行代码片段,它更好地说明了我想要完成的任务:
// Prototype with nested properties.
var prototype = {
question: 'Am I nested?',
nested: {
answer: 'Yes.',
thoughts: 'I like being nested.'
}
};
// Another prototype. Overrides properties.
var liar = Object.create(prototype);
liar.nested.answer = 'N-No...!'; // Modifies prototype since
// liar.nested === prototype.nested
// I could do this, but then I lose the other nested properties.
var indecisive = Object.create(prototype);
indecisive.nested = { answer: 'I dunno?' }; // New object, won't delegate.
// Output some text on the snippet results panel.
function results(text) { results.element.appendChild(document.createTextNode(text + '\n')); }; results.element = document.getElementById('results'); results.json = function() { for(var i = 0, len = arguments.length; i < len; ++i) { results(JSON.stringify(arguments[i], results.json.replacer, results.json.indentation)); } }; results.json.replacer = function(k, v) { return typeof v === 'undefined' ? 'undefined' : v; }; results.json.indentation = 4;
results.json(
prototype.nested.answer, // Shouldn't have been changed.
liar.nested.answer, // As expected.
indecisive.nested.answer, // As expected.
prototype.nested.thoughts, // As expected.
liar.nested.thoughts, // As expected.
indecisive.nested.thoughts, // Undefined, when it should delegate to the prototype.
prototype, // It's been modified.
liar, // It's empty. It should have an own property.
indecisive // Has own property, but nested object does not delegate.
);
<html>
<body>
<pre id="results"></pre>
</body>
</html>
这个问题很复杂,因为nested
属性引用了原型和链接到它的对象中的同一个对象。如果我分配给object.nested.answer
,则会更改object.nested
,这与prototype.nested
引用的对象相同。
这样的赋值会影响原型链中的所有对象。我希望它是一个本地的变化。我希望将新的object.nested.answer
层次结构创建为object
的自己的属性。
我可以为object.nested
分配一个新对象。但是,虽然这会产生在object
上创建自己的属性的预期效果,但它也会覆盖object.nested.thoughts
。为了解决这个问题,我必须通过提供prototype.nested.thoughts
副本作为新对象的一部分来重复自己。
这会破坏参照完整性。如果我故意在运行时更改prototype.nested.thoughts
以更改所有未故意覆盖该属性的对象,则object
将不会更改;它会继续引用自己的本地副本nested.thoughts
。
所以,我想要做的是让嵌套对象委托对其父级原型的属性访问。可以这样做吗?如果是这样,怎么样?
我正在写一些类似于小型几何库的东西。我认为我的API设计过于富有创意,遇到了一些限制,找不到任何与我试图做的相匹配的模式,最后问了这个问题,看看是否有任何已知的解决方案。
这是我的思路和最后的可执行片段:
tuple
原型。我根据tuple
s。
┌─────────────┬────────┬────────────────────────┐
│ Abstraction │ Pair │ Description │
├─────────────┼────────┼────────────────────────┤
│ Coordinates │ (x, y) │ Cartesian coordinates. │
│ Dimensions │ (w, h) │ Width and height. │
└─────────────┴────────┴────────────────────────┘
我认为给每个抽象提供一个不同的符号会很好。
toString
取决于prototype
的属性。(x, y)
,这已经是默认的tuple
符号。dimensions
表示为|w, h|
。这使我找到了以下符号结构:
┌─────────────┬────────────────────┬───────────────────────────────────┐
│ Abstraction │ Notation │ Grammar │
├─────────────┼────────────────────┼───────────────────────────────────┤
│ Tuple │ (x₀, x₁, ... , xᵢ) │ '(' element ( ', ' element )* ')' │
╞═════════════╪════════════════════╪═══════════════════════════════════╡
│ Coordinates │ (x, y) │ Inherits the grammar of Tuple. │
├─────────────┼────────────────────┼───────────────────────────────────┤
│ Dimensions │ |w, h| │ '|' element ( ', ' element )* '|' │
└─────────────┴────────────────────┴───────────────────────────────────┘
// 1. Tuple prototype.
var tuple = {
prototype: {
toString: function tuple_toString() {
return '(' + this.elements.join(', ') + ')';
}
},
new: function tuple_new() {
var tuple = Object.create(this.prototype);
tuple.elements = Array.prototype.slice.call(arguments);
return tuple;
}
};
// 2. Geometric concepts.
var coordinates = {
prototype: Object.create(tuple.prototype),
new: tuple.new
};
var dimensions = {
prototype: Object.create(tuple.prototype),
new: tuple.new
};
// 3.1 Prototype properties in the toString function.
tuple.prototype.toString = function tuple_toString() {
var elements = this.elements,
notation = this.notation, // Should be inherited from the prototype.
join = notation.join
brackets = notation.brackets,
open = brackets.open,
close = brackets.close;
return open + elements.join(join) + close;
};
// 3.4 Notational metadata in prototype.
tuple.prototype.notation = {
brackets: {
open: '(',
close: ')'
},
join: ', '
};
dimensions.prototype.notation = {
brackets: {
open: '|',
close: '|'
}
// Is the ', ' from tuple.prototype.notation.join inherited?
};
// Output some text on the snippet results panel.
function results(text) { results.element.appendChild(document.createTextNode(text + '\n')); }; results.element = document.getElementById('results'); results.json = function() { for(var i = 0, len = arguments.length; i < len; ++i) { results(JSON.stringify(arguments[i], results.json.replacer, results.json.indentation)); } }; results.json.replacer = function(k, v) { return typeof v === 'undefined' ? 'undefined' : v; }; results.json.indentation = 4;
var triplet = tuple.new(1, 2, 3);
var origin = coordinates.new(0, 0);
var fullHD = dimensions.new(1920, 1080);
results.json(
triplet.toString(), // (1, 2, 3) - As expected.
origin.toString(), // (0, 0) - As expected.
fullHD.toString(), // |1920,1080| - Where is the space?
triplet.notation.join, // ', ' - As expected.
origin.notation.join, // ', ' - As expected.
fullHD.notation.join // undefined
);
<html>
<body>
<pre id="results"></pre>
</body>
</html>
fullHD.notation.join
正在返回undefined
。当它作为参数传递给Array.prototype.join
时,它就像没有传递任何参数一样,导致函数的默认行为。
我希望它委托给tuple.prototype.notation.join
,然后返回', '
。