【问题标题】:How should I structure inheritance for PouchDB persistence?我应该如何构造 PouchDB 持久性的继承?
【发布时间】:2019-09-16 04:03:40
【问题描述】:

我正在用 JS 编写一个相关对象的集合,我想将这些对象存储在 PouchDB 中,但默认的原型继承行为与持久层不兼容。我怎样才能拥有一个结构,它可以从父级继承一些属性作为可枚举的 OwnProperties,但其他属性(函数)如原型?我需要使一些属性可写/可配置和一些冻结,并且我将拥有 10e5-10e6 对象,因此牢记内存占用和性能至关重要。

PouchDB 将接受任何 JS 对象并抓取其可枚举属性,跳过原型属性。这很有意义,而且 PouchDB 很棒。 我的对象是一个家庭,它们与每一代共享一些属性和方法,每个都添加一些,并保留所有父级的。

为了简单起见,我将使用 Shapes 比喻。

Shape.properties={
  x:0,                                        // Vanilla x displacement
  y:0,                                        // Vanilla y displacement 
  color:red,                                  // We want this to be immutable
};
Shape.methods={
  move:function(x,y){this.x+=x; this.y+=y;},  // Change displacement
  reset:function(){this.x=0; this.y=0;},      // Back to origin
  save:function(){db.put(this);},             // Persist to DB
}

Rectangle.properties={
  w:2,                                        // Width
  h:1,                                        // Height
};
Rectangle.methods={
  aspect:function(w,h){this.w=(w/h)*this.h;}, // Stretch 
};

Cube.properties={
  z:0,                                        // Elevation
};

Cube.methods={
  lift:function(z){this.z+=z;};               // Float up
};

如果我使用普通的 JS 原型继承并制作一个 Cube,它将只有 z 的 OwnProperties,这使得持久性毫无用处。 所以我制作了一个帮助模块来解决这个问题,它具有我可以使用位掩码和组合函数将所有部分拼凑在一起的快速 PropertyDescriptors 的功能:


    // writable=4 | enumerable=2 | configurable=1
    /* Process a bitMask that describes the desired property descriptor. We then use the combine method to add 
     * specifically described properties to an object. PouchDB will grab any enumerable properties, some should be write-protected 
     * but vanilla properties are Accessors with bitmask 6(enumerable and writable). Based on code from elsewhere. */

    accessor:function(bMask,val){ // Accessor descriptor: bitmask , value
        return {configurable:Boolean(bMask & 1),enumerable:Boolean(bMask & 2),writable:Boolean(bMask & 4),value:val};
        },

    combine:function(root,properties,methods){                       // This is a naive implementation, ask SO for help? 

        for (let i in root){                                         // Iterate over properties.
           properties[i]=Object.getOwnPropertyDescriptor(root,i);    // Combine the root properties and the given properties objects, 
        }                              
        methods=Object.defineProperties(Object.getPrototypeOf(root),methods);// Add the methods to the root prototype.               
        return Object.create(methods,properties);                    // Combine the prototype and the properties
    },

我的结构现在看起来像

Shape.properties={
  x:accessor(6,0),
  y:accessor(6,0),
  color:accesor(2,red),                                   // Bitmask 2: enumerable only
};
Shape.methods={
  move:accessor(0,function(x,y){this.x+=x; this.y+=y;}),   // Bitmask 0: 'Frozen' property
  reset:accessor(0,function(){this.x=0; this.y=0;}),
  save:accessor(0,function(){db.put(this);}),
}
var Shape=function(){
  return combine(new Object(),Shape.properties, Shape.methods);
};


Rectangle.properties={
  w:accessor(6,0),
  h:accessor(6,0),
};
Rectangle.methods={
  aspect:accessor(0,function(w,h){this.w=(w/h)*this.h;}),
};

var Rectangle=function(){
  return combine(new Shape(),Rectangle.properties, Rectangle.methods);
};

//...

所以这行得通,但是相同类型的两个对象不再共享一个原型,也就是说每个对象都有一个唯一的原型实例。

这是对记忆的废话,通常是不好的做法。我也可以为每个对象编写方法,然后让 pouch 将它们作为错误吐回给我。

我查看了其他选项并遇到了this article,它有一些很好的线索,但后来我读到了this,这让我觉得也许我应该创建静态/冻结原型对象并从那里构建或采用不同的方法完全。

我也研究过使用 Object.assign() 但它可以大量修改被复制的属性。

很可能有一个我没有看到的简单解决方案,你知道它是什么吗?或者你能帮我找到吗?

【问题讨论】:

    标签: javascript inheritance prototype mixins pouchdb


    【解决方案1】:

    因此,在环顾四周并进行更多试验后,我发现冻结的原型似乎可以工作,假设它不会触发上面提到的优化错误。我现在的结构是这样的

    const methods={},properties={};
    
    properties.Shape=Object.freeze({
        x:{value: 0, writable: true, enumerable: true, configurable: true},
        y:{value: 0, writable: true, enumerable: true, configurable: true}, 
        color:{value: 'red', writable: false, enumerable: true, configurable: false},   
    });
    
    methods.Shape=Object.freeze({
            move:{value:function(x, y) {
            this.x += x;
            this.y += y;
            console.info('Shape moved.');
            return this;},writable: true, enumerable: true, configurable: true},
    });
    
    const Shape=function() {return Object.defineProperties(this,properties.Shape);}
    Shape.prototype=Object.freeze(Object.defineProperties({},methods.Shape));
    
    
    
    properties.Rectangle=Object.freeze({
        w:{value: 0, writable: true, enumerable: true, configurable: true},
        h:{value: 0, writable: true, enumerable: true, configurable: true},
      });
    
      methods.Rectangle=Object.freeze({
        zoom:{value:function(z){
        this.w=this.w*z;
        this.h=this.h*z;
        console.info('Rectangle zoomed');
        return this;},writable: true, enumerable: true, configurable: true},
    });
    
    const Rectangle=function() {    Shape.call(this); return Object.defineProperties(this,properties.Rectangle);};
    Rectangle.prototype = Object.freeze(Object.create(Shape.prototype,methods.Rectangle));
    
    var rect = new Rectangle();
    
      console.log('Is rect an instance of Rectangle?',
        rect instanceof Rectangle); // true
      console.log('Is rect an instance of Shape?',
        rect instanceof Shape); // true
      rect.move(1, 1); // Outputs, 'Shape moved.'
      rect.zoom(2); // {x:1,y:1,w:0,h:0}
      rect2=new Rectangle();
      console.log('Do rect2 and rect share a prototype?',
      rect2.prototype===rect.prototype); // yes
      shape=new Shape();
      shape.move(2,2);  // moves
      shape.zoom(2);    // Fails as expected
    

    用辅助函数清理它应该使它更漂亮,更容易更新。

    【讨论】:

      猜你喜欢
      • 2016-03-07
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 2015-10-18
      • 2018-07-25
      • 1970-01-01
      • 2010-09-29
      • 2010-09-18
      相关资源
      最近更新 更多