【发布时间】: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