Javascript 在ES6中生成与调用实例相同类的对象
我有一个从Array扩展而来的类'Grid',其目的是实现一些用于双向数组的方法,它目前没有'constructor'函数。为了简洁起见,我只展示了有问题的函数:Grid.getPlane,它返回一个用参数构造的子网格Javascript 在ES6中生成与调用实例相同类的对象,javascript,oop,ecmascript-6,Javascript,Oop,Ecmascript 6,我有一个从Array扩展而来的类'Grid',其目的是实现一些用于双向数组的方法,它目前没有'constructor'函数。为了简洁起见,我只展示了有问题的函数:Grid.getPlane,它返回一个用参数构造的子网格 class Grid extends Array { //... getPlane(width, height, x, y) { let myPlane = new Grid; // calculations... return myPlane;
class Grid extends Array {
//...
getPlane(width, height, x, y) {
let myPlane = new Grid;
// calculations...
return myPlane;
}
//...
}
我还有另外一个类“地形”,从这个类扩展而来。这一个旨在为地形数据提供一些更具体的功能。预期的功能是,每当我调用类“Terrain”实例的“getPlane”函数时,返回的对象也属于“Terrain”类,因此我可以使用该类特有的函数。但正如您所预测的,我要么使用从“Grid”继承的函数声明,得到一个网格而不是地形,要么覆盖该函数,留下丑陋的重复代码:
class Terrain extends Grid {
//...
getPlane(width, height, x, y) {
let myPlane = new Terrain;
// calculations...
return myPlane;
}
//...
}
我试图在中使用Object.create,但:
let myPlane = Object.create(this.prototype)
返回未定义的。
及
给我一个名为“Terrain”的对象,它的行为不像数组。
Object.create是否有办法为我获取与“this”对象相同类的对象?或者以任何其他方式生成具有相同类的对象?这是一种以下情况:
做得太多的方法
缺乏对超能力方法的呼唤
深度嵌套的层次结构是编写可维护系统的痛苦方式;乘以4000x,单位为JS
第一:
// 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;
}
}
您将注意到克隆方法不是继承的。他们专注于实例化。它们基本上是工厂,实际上是一个独立对象的领域,或者是一个静态方法,比如make | from | of | empty | unit
然后他们调用setDetails,这是一个实例方法,它基本上执行构造函数应该执行的操作,或者工厂应该执行的操作,并且具有继承的行为
说到干,继承是一种可怕的保持这种状态的方式。就从我所写的内容来看,有多少行专门用于覆盖构造函数clone、make或调用父级super,甚至只是担心扩展,仅仅因为
这让我想到了另一种模式:库、纯函数/方法和装饰
如果您不关心实际类型和原始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矢量
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 }