我有以下类'Grid'从Array扩展,目的是为bidimentional数组实现一些方法,它目前没有'constructor'函数。为了简洁起见,我只显示了有问题的函数:Grid.getPlane,它返回一个用参数构造的子网格。
class Grid extends Array {
//...
getPlane(width, height, x, y) {
let myPlane = new Grid;
// calculations...
return myPlane;
}
//...
}
然后我有另一个类'Terrain'从这个延伸。这个旨在为地形数据提供一些更具体的功能。预期的功能是,每当我调用“Terrain”类的实例的“getPlane”函数时,返回的对象也是“Terrain”类(因此我可以使用特定于此类的函数)。但正如你可以预测的那样,我要么使用'Grid'中的继承函数声明并获得一个Grid(而不是Terrain),要么覆盖该函数,让我留下难看的重复代码:
class Terrain extends Grid {
//...
getPlane(width, height, x, y) {
let myPlane = new Terrain;
// calculations...
return myPlane;
}
//...
}
我尝试使用Object.create但是:
let myPlane = Object.create(this.prototype)
返回undefined。 并且
let myPlane = Object.create(this.constructor.prototype)
给我一个名为'Terrain'的对象,它的行为与Array不同。 Object.create有没有办法让我得到与'this'对象相同类的对象?或任何其他方式生成具有相同类的对象?
答案 0 :(得分:1)
这是一个案例:
super
首先:
// assume
class A extends Array {
doX (x) {
console.log(`A.doX(${x})`);
return new A();
}
}
class B extends A {
doX (x, y) {
super.doX(x);
console.log(`B.doX(${x}, ${y})`);
return new B();
}
}
class C extends B {
doX (x, y, z) {
super.doX(x, y);
console.log(`C.doX(${x}, ${y}, ${z})`);
return new C();
}
}
const c = new C();
c.doX(1, 2, 3);
// "A.doX(1)"
// "B.doX(1, 2)"
// "C.doX(1, 2, 3)"
现在,我应该期望在运行此代码时没有任何问题 不过,我会有一段时间实际使用它。
为什么呢?因为我决定重载方法同时担心构造和逻辑。
A类将始终返回A,B类将始终返回B,尽管超级调用实际上返回一个完全不同的对象(新的A)。 C返回一个新的C,尽管它已经创建了A和B(但没有引用A)。
那你做什么?
有几点想法:
所以让我们尝试构建分离
class Person {
static make () { return new Person(); }
setDetails (name, age) {
this.name = name;
this.age = age;
}
clone (cfg = this) {
const person = Person.make();
person.setDetails(cfg.name, cfg.age);
return person;
}
}
class Employee extends Person {
static make () { return new Employee(); }
setDetails (name, age, id) {
super.setDetails(name, age);
this.id = id;
}
clone (cfg = this) {
const employee = Employee.make();
employee.setDetails(cfg.name, cfg.age, cfg.id);
return employee;
}
}
您注意到clone
方法不会被继承。他们专注于实例化。它们基本上是工厂(实际上是单独对象的领域,或静态方法,如make|from|of|empty|unit
)。
然后他们调用setDetails
这是一个实例方法,它基本上是做构造函数应该做的,或工厂应该做什么,而做继承行为。< / p>
谈到DRY,继承是保持这种方式的可怕方式。正如我所写的那样,有多少行专门用于覆盖构造函数(clone
,make
),或者向父母调用(super
)甚至只是担心关于扩展,只是因为?
这让我有了一个不同的模式:图书馆,纯粹的功能/方法和装饰。
如果你不关心实际&#34;键入&#34; (在原始JS中,虽然在控制台中很有用,但你不应该,因为它在其他地方没用),那么你就可以快乐地生成以数据为中心的对象。
结构,就像你在C,Go,或Python等中看到的那样。
然后,您可以自由地编写您可能希望在库/服务上使用的所有可重用计算,这些计算/服务可用于这些结构(或理想情况下是其副本)。
class Vector {
static make2D (x = 0, y = 0) {
return { x, y };
}
static make3D (x = 0, y = 0, z = 0) {
return { ...Vector.make2D(x, y), z };
}
static add2D (v1, v2) {
return Vector.make2D(v1.x + v2.x, v1.y + v2.y);
}
}
const vectors = [
{ x: 0, y: 1 },
{ x: 32, y: 8 },
{ x: 10, y: 12 },
{ x: 0, y: 0 },
];
const vectorSum = vectors.reduce(Vector.add2D, Vector.make2D());
vectorSum; // { x: 42, y: 21 }
如果您真的需要输入它们,那么您可以执行以下操作:
class Vector {
add2D (
{x: x1, y: y1},
{x: x2, y: y2}
) {
return Vector2D.of(x1 + x2, y1 + y2);
}
}
class Vector2D {
constructor (x, y) {
return Object.assign(this, { x, y });
}
static of (x, y) { return new Vector2D(x, y); }
static from ({ x, y }) { return Vector2D.of(x, y); }
static empty () { return Vector2D.of(0, 0); }
}
const vector = vectors
.map(Vector2D.from)
.reduce(Vector.add2D, Vector2D.empty());
// Vector2D { x: 42, y: 21 }
您很难抱怨您的代码在此时没有干。您甚至可以将5D向量放入条目数据中,然后输出正确的2D向量...