试图理解以工厂和基于函数的混合技术为特征的对象组合模式

Trying to understand an object composition pattern which features a factory and a function based mixin technique

我正在尝试了解 JavaScript 中基于函数的组合的行为。

const Animal = (name) => {
  let properties = { name };
  return ({
    get name() { return properties.name },
    set name(newName) { properties.name = newName },
    breathe: function() {console.log(`${this.name} breathes!`); }
  })
}

const aquaticKind = (animal) => ({
  swim: () => console.log(`${animal.name} swims`)
})

const walkingKind = (animal, noOfLegs) => {
  const properties = { noOfLegs }
  return ({
    get noOfLegs() { return properties.noOfLegs },
    set noOfLegs(n) { properties.noOfLegs = n; },
    walk: () => console.log(`${animal.name} walks with ${properties.noOfLegs} legs`)
  })
}

const egglayingKind = (animal) => ({
  layEgg: () => console.log(`${animal.name} laid an egg`)
})

const Crocodile = (name) => {
  const info = Animal(name);
  return Object.assign(info,
                       walkingKind(info, 4),
                       aquaticKind(info),
                       egglayingKind(info)
                      );
}
const snooty = Crocodile('snooty');
snooty.breathe();
snooty.swim();
snooty.walk();
snooty.name = "coolie";
snooty.noOfLegs = 23 // I expected this to get update to 23
snooty.swim();
snooty.walk();
snooty.layEgg();

如果您 运行 上面的代码,您会看到 noOfLegs 永远不会更新,而 name 会更新。我似乎无法解决这个问题。我们如何让 noOfLegs 也得到更新?

MDN Documentation for object.assign 向您展示如何复制“accessors”

这是按预期工作的代码 - completeAssign 函数完全基于 link

中的代码

const completeAssign = (target, ...sources) => {
    sources.forEach(source => {
        const descriptors = Object.keys(source).reduce((descriptors, key) => {
            descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
            return descriptors;
        }, {});
        Object.getOwnPropertySymbols(source).forEach(sym => {
            const descriptor = Object.getOwnPropertyDescriptor(source, sym);
            if (descriptor.enumerable) {
                descriptors[sym] = descriptor;
            }
        });
        Object.defineProperties(target, descriptors);
    });
    return target;
};

const Animal = (name) => {
    const properties = { name };
    return ({
        get name() { return properties.name },
        set name(newName) { properties.name = newName },
        breathe () { console.log(`${this.name} breathes!`); }
    })
}

const aquaticKind = (animal) => ({ 
    swim: () => console.log(`${animal.name} swims`) 
});

const walkingKind = (animal, noOfLegs) => {
    const properties = { noOfLegs };
    return ({
        get noOfLegs() { return properties.noOfLegs },
        set noOfLegs(n) { properties.noOfLegs = n; },
        walk: () => console.log(`${animal.name} walks with ${properties.noOfLegs} legs`)
    })
}

const egglayingKind = (animal) => ({
    layEgg: () => console.log(`${animal.name} laid an egg`)
})

const Crocodile = (name) => {
    const info = Animal(name);
    return completeAssign(info,
        walkingKind(info, 4),
        aquaticKind(info),
        egglayingKind(info)
    );
}
const snooty = Crocodile('snooty');
snooty.breathe();
snooty.swim();
snooty.walk();
snooty.name = "coolie";
snooty.noOfLegs = 23;
snooty.swim();
snooty.walk();
snooty.layEgg();