覆盖原型链

时间:2015-11-01 06:21:48

标签: javascript object nested prototype prototype-chain

我有以下嵌套数据结构:

{ 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设计过于富有创意,遇到了一些限制,找不到任何与我试图做的相匹配的模式,最后问了这个问题,看看是否有任何已知的解决方案。

这是我的思路和最后的可执行片段:

  1. 在解析几何中,有序的数字列表很常见。所以,我创建了一个tuple原型。
  2. 我根据tuple s。

    将其他几何概念定义为抽象
    ┌─────────────┬────────┬────────────────────────┐
    │ Abstraction │  Pair  │      Description       │
    ├─────────────┼────────┼────────────────────────┤
    │ Coordinates │ (x, y) │ Cartesian coordinates. │
    │  Dimensions │ (w, h) │ Width and height.      │
    └─────────────┴────────┴────────────────────────┘
    
  3. 我认为给每个抽象提供一个不同的符号会很好。

    1. 为了做到这一点,我toString取决于prototype的属性。
    2. 坐标通常表示为(x, y),这已经是默认的tuple符号。
    3. 我决定将dimensions表示为|w, h|
    4. 这使我找到了以下符号结构:

      ┌─────────────┬────────────────────┬───────────────────────────────────┐
      │ Abstraction │      Notation      │              Grammar              │
      ├─────────────┼────────────────────┼───────────────────────────────────┤
      │       Tuple │ (x₀, x₁, ... , xᵢ) │ '(' element ( ', ' element )* ')' │
      ╞═════════════╪════════════════════╪═══════════════════════════════════╡
      │ Coordinates │       (x, y)       │ Inherits the grammar of Tuple.    │
      ├─────────────┼────────────────────┼───────────────────────────────────┤
      │  Dimensions │       |w, h|       │ '|' element ( ', ' element )* '|' │
      └─────────────┴────────────────────┴───────────────────────────────────┘
      
  4. // 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,然后返回', '

0 个答案:

没有答案