生成与ES6

时间:2017-01-31 05:38:23

标签: javascript oop ecmascript-6

我有以下类'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'对象相同类的对象?或任何其他方式生成具有相同类的对象?

1 个答案:

答案 0 :(得分:1)

这是一个案例:

  1. 做得太多的方法
  2. 缺乏对super
  3. 方法的呼唤
  4. 深层嵌套的层次结构是编写可维护系统的痛苦方式;在JS
  5. 中乘以4000x

    首先:

    // 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,继承是保持这种方式的可怕方式。正如我所写的那样,有多少行专门用于覆盖构造函数(clonemake),或者向父母调用(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向量...