Javascript 中的组合模式与 类 但没有 Mixins?

Composition Pattern in Javascript with Classes but without Mixins?

以下是在 Javascript 中实现组合模式的有效策略吗?我想使用 类 而不是构造函数或普通对象,而且我知道 Mixins 不是最佳实践。一个问题是,在这种方法中,添加到 Person 对象的方法没有附加到原型,因此每个方法都需要分配内存。谢谢!

class Person {
  name;
  constructor(name) {
    this.name = name;
  }
}

function fly() {
  return {
    fly() {
      console.log(`${this.name} can fly!`);
    },
  };
}

function swim() {
  return {
    swim() {
      console.log(`${this.name} can swim!`);
    },
  };
}

function makeFlyingPerson(name) {
  return Object.assign(new Person(name), fly());
}

function makeSwimmingPerson(name) {
  return Object.assign(new Person(name), swim());
}

...the methods added to Person objects are not attached to the prototype and therefore each require memory allocation

是的,但这是一个微不足道的数量,每个对象每个方法一个 属性 的成本(保存方法的函数引用)。属性不是,但它们并不大。为避免疑义:function 对象被所有实例重用,而不是被复制。

flyswim 没有理由成为函数(至少,none 从问题中可以明显看出),直接使用对象即可:

class Person {
    name;
    constructor(name) {
        this.name = name;
    }
}

const flyMethods = {
    fly() {
        console.log(`${this.name} can fly!`);
    },
};

const swimMethods = {
    swim() {
        console.log(`${this.name} can swim!`);
    },
};

function makeFlyingPerson(name) {
    return Object.assign(new Person(name), flyMethods);
}

function makeSwimmingPerson(name) {
    return Object.assign(new Person(name), swimMethods);
}

请注意,这仍在使用 mixin(您的原始版本和上面的版本)。

除非您打算将 fly/flyMethodsswim/swimMethodsPerson 以外的其他 类 重用,尽管,使用 extends 看起来更简单,并且会给你原型方法重用:

class FlyingPerson extends Person {
    fly() {
        // ...
    }
}

如果你正在重用fly/flyMethods等,有多个类,另一种选择是建厂从各种方法集创建原型然后重用它的函数:

class Person {
    name;
    constructor(name) {
        this.name = name;
    }
}

const flyMethods = {
    fly() {
        console.log(`${this.name} can fly!`);
    },
};

const swimMethods = {
    swim() {
        console.log(`${this.name} can swim!`);
    },
};

function extendWith(cls, name, ...mixins) {
    // We use the wrapper object so that the class constructor's name is assigned from `name`
    const obj = {
        [name]: class extends cls {
        }
    };
    Object.assign(obj[name].prototype, ...mixins);
    return obj[name];
}

const FlyingPerson = extendWith(Person, "FlyingPerson", flyMethods);
const SwimmingPerson = extendWith(Person, "SwimmingPerson", swimMethods);
const FlyingSwimmingPerson = extendWith(Person, "FlyingSwimmingPerson", flyMethods, swimMethods);

const joe = new FlyingSwimmingPerson("Joe");
joe.fly();
joe.swim();

class Animal {
    name;
    type;
    constructor(name, type) {
        this.name = name;
        this.type = type;
    }
}

const FlyingSwimmingAnimal = extendWith(Animal, "FlyingSwimmingAnimal", flyMethods, swimMethods);

console.log(FlyingSwimmingAnimal.name); // FlyingSwimmingAnimal
const splippery = new FlyingSwimmingAnimal("Slippery");
splippery.fly();
splippery.swim();