Build a chainable calculator with a private, read-only value
Implement createCalculator() returning an object that supports chaining: createCalculator().add(7).div(2) leaves its value at 3.5, read via .value. Requirements: value is read-only from outside (assigning to it must not change the running total); div(0) throws Error('Cannot divide by zero') and must NOT alter the value.
function createCalculator() {
// your code here
}
Write the implementation.
Keep the running total in a closure variable. Return an object whose add/div mutate it and return this for chaining. Expose the total through a get value() accessor only — with no setter, calc.value = 100 is silently ignored, so the state stays private. In div, check the divisor and throw before dividing, so a divide-by-zero leaves the total untouched.
- ✗Exposing the total as a writable property instead of a getter, so outside code can corrupt it
- ✗Dividing before the zero check, so
div(0)mutates the value (toInfinity/NaN) before throwing - ✗Returning the number from
add/divinstead ofthis, breaking the chain
- →How does a getter-only
valuekeep the total private compared with a public field? - →How would
#privateclass fields achieve the same encapsulation as the closure variable?
Solution
The total lives in a closure variable; methods mutate it and return this, and value is a getter only.
function createCalculator() {
let total = 0;
return {
get value() {
return total;
},
add(x) {
total += x;
return this;
},
div(x) {
if (x === 0) throw new Error('Cannot divide by zero');
total /= x;
return this;
},
};
}
createCalculator().add(7).div(2).value; // 3.5
How it works
total is declared inside createCalculator and unreachable from outside — that is the private state via closure. It is exposed only through the value getter with no setter: an attempt at calc.value = 100 is silently ignored (or throws in strict mode), so the total cannot be corrupted.
add and div mutate total and return this, which is what makes the calls chain. In div, the divisor check runs before the division: on zero it throws and never reaches total /= x, so the value is left unchanged. </content>