Issue
I’m trying to understand behavior of function based composition in 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();
If you run the code above, you will see that noOfLegs
never get updated, while name
get updated. I can’t seem to wrap my head around this. How do we make noOfLegs
get updated too?
Solution
MDN Documentation for object.assign shows you how to copy "accessors"
Here’s your code that works as expected – the completeAssign
function is based entirely on the code in that 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();
Answered By – Bravo
Answer Checked By – Dawn Plyler (BugsFixing Volunteer)