Javascript 我应该如何构造数据库持久性的继承?
我正在用JS编写一个相关对象的集合,我想将其存储在PockDB中,但是默认的原型继承行为不适合持久层。我怎样才能拥有一个结构,它可以从父级继承一些属性作为可枚举的OwnProperties,但也可以继承其他属性(函数),比如原型?我需要将一些属性设置为可写/可配置,一些属性设置为冻结,我将使用10e5-10e6对象,因此记住内存占用和性能是至关重要的 PockDB将接受任何JS对象并删除其可枚举属性,跳过原型属性。这很有道理,而且非常棒。 “我的对象”是一个族,它与每一代共享一些属性和方法,每一代添加一些属性和方法,同时保留所有父代的属性和方法 为了简单起见,我将使用形状比喻Javascript 我应该如何构造数据库持久性的继承?,javascript,inheritance,prototype,mixins,pouchdb,Javascript,Inheritance,Prototype,Mixins,Pouchdb,我正在用JS编写一个相关对象的集合,我想将其存储在PockDB中,但是默认的原型继承行为不适合持久层。我怎样才能拥有一个结构,它可以从父级继承一些属性作为可枚举的OwnProperties,但也可以继承其他属性(函数),比如原型?我需要将一些属性设置为可写/可配置,一些属性设置为冻结,我将使用10e5-10e6对象,因此记住内存占用和性能是至关重要的 PockDB将接受任何JS对象并删除其可枚举属性,跳过原型属性。这很有道理,而且非常棒。 “我的对象”是一个族,它与每一代共享一些属性和方法,每一
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原型继承并创建一个多维数据集,它将只具有z的OwnProperties,这使得持久性毫无用处。
因此,我制作了一个帮助器模块来解决这个问题,它有一些函数,我可以使用它们来制作快速属性脚本,使用位掩码和组合函数将所有部分拼凑在一起:
// 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);
};
//...
这是可行的,但是两个相同类型的对象不再共享一个原型,也就是说每个对象都有一个唯一的原型实例
这是对记忆力的浪费,而且通常是不好的练习。我也可以把方法写在每个对象上,然后让Pocket把它们作为错误吐出来
我研究了其他的选择,发现其中有一些很好的线索,但后来我读到了,这让我觉得也许我应该创建静态/冻结的原型对象,然后从那里构建,或者采取完全不同的方法
我还研究了如何使用Object.assign(),但它可以大量修改要复制的属性
很可能有一个我没有看到的简单解决方案,你知道它是什么吗?或者你能帮我找到它吗?因此,在环顾四周并进行更多实验后,我发现一个冻结的原型似乎可以工作,假设这不会触发上面提到的优化错误。我现在的结构是
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
用helper函数清理它应该会使它更漂亮,更容易更新 因此,在环顾四周并进行更多实验后,我发现一个冻结的原型似乎可以工作,假设这不会触发上面提到的优化错误。我现在的结构是
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
用helper函数清理它应该会使它更漂亮,更容易更新