Hi, I’m new to JS and coding in general. Trying to get a better grasp of objects and factory functions. Can someone help explain why the factory function below doesn’t update the output when a property is changed?
//this is a factory function. it is a function that returns a new object
function circleArea(radius = 10) {
return {
radius: radius,
get area(){
return radius ** 2 * Math.PI;
},
};
};
// this is an object made from Factory function
const a = circleArea(12);
console.log(a);
a.radius = 10;
console.log(a.area);
//this function doesn't update the area when radius is changed
In the function radius is always 12 in the getter because that is what it was created with.
It’s not referring to the radius in the object returned. To reference the object use this.radius in the getter and it works.
This is helping me understand better. I needed it to reference the new parameter of the object by calling “this”. Here is the amended code that works for anyone’s reference.
//this is a factory function. it is a function that returns a new object
function circleArea(radius = 10) {
return {
radius: radius,
get area(){
return this.radius ** 2 * Math.PI;
},
};
};
// this is an object made from Factory function
const a = circleArea(12);
console.log(`radius of ${a.radius} = ${a.area}`);
//this changes the radius and returns a new area
a.radius = 10;
console.log(`radius of ${a.radius} = ${a.area}`);
The issue here is that the whole point of a factory function is to protect that variable, and to provide accessors to and from it. Objects in javascript traditionally don’t support private properties, so factories are a great workaround.
The same factory that doesn’t expose the radius directly can be defined like this:
function circleArea(radius=10){
// here, we have a variable radius.
return {
get radius(){
// This is accessing the variable inside the closure
return radius;
}
set radius(value){
// by defining a setter method on our object, we can "gatekeep" for
// our private variables, like radius. In this case, we error for a
// negative value.
if(value<0) throw new Error("Negative radius not supported, bud.");
radius=value;
}
get area(){
// again, we simply access the variable, *not* the object property!
return radius ** 2 * Math.PI;
}
}
const a = circleArea(12);
console.log(`Radius of a: ${a.radius}, area: ${a.area}`);
// we are using the getter on the right, and the setter on the left!
a.radius = a.radius*1.5;
console.log(`Radius of a: ${a.radius}, area: ${a.area}`);
So we define getters and setters for radius. And from the outside, it looks like a property on our object - but in our object definition, we can see that we are using these as privileged methods to get a private variable within our factory.
No worries, and yes you’re exactly right. Symbols and weakMaps can provide much the same result.
The advantage to pure factories over objects, for me, is the explicit structure of a scope tree over the slippery nature of context. The this reference can change, not only based on where you call it from, but what you call it on [lexical context vs execution context].
A great discussion on balancing functional and object oriented coding has been assembled by Eric Elliott. A series of Medium blog posts have become the book Composing Software, well worth the read.