- Published on
Discard after usages
Have you ever noticed food labels warning you to discard the contents after a certain number of days? That made me wonder: how can we create a variable in JavaScript that automatically discards its content after X uses? Let's explore some implementations.
Encapsulate within a closure function
A closure is a simple and effective way to encapsulate state. By maintaining a usage counter within the closure, we can track the number of times a value is accessed.
function createPromoCode(validCode, maxUsages) {
// Closure keeps state private, avoiding external modifications.
let usageCounter = 0;
return function() {
if (usageCounter < maxUsages) {
usageCounter++;
return validCode;
}
return null;
};
}
const promo = createPromoCode('free beer 🍻😆', 3);
console.log(`You are getting a ${promo()}`);
console.log(`You are getting a ${promo()}`);
console.log(`You are getting a ${promo()}`);
console.log(`You are getting a ${promo()}`);
Place inside a Getter/Setter
JavaScript ES5 has getter and setter syntax, which fits very well as a solution.
Object.defineProperty(window, 'discardAfterUsed', {
get() {
if (this.read) {
return null;
}
this.read = true;
return this.value;
},
set(value) {
this.read = false;
this.value = value;
},
});
window.discardAfterUsed = 'one-time free beer'
console.log(`You are getting a ${discardAfterUsed}`);
console.log(`You are getting a ${discardAfterUsed}`);
While defining a property on the window object is quick for demonstration purposes, it's not a practical solution.
Implement a class
A more structured approach involves creating a class, making it reusable and clear.
class DiscardAfterUsed {
constructor(value) {
this._value = value;
}
get value() {
if (this._value !== null && this._value !== undefined) {
const temp = this._value;
this._value = null; // Ready for garbage collector
return temp;
}
return null;
}
}
const onceOnly = new DiscardAfterUsed('one-time free beer');
console.log(`You are getting a ${onceOnly.value}`);
console.log(`You are getting a ${onceOnly.value}`);
Use proxy
Using a proxy is often more complex than other solutions, but it is flexible and does not modify the original value.
const discardAfterUsedHandler = {
get: function(target, prop, receiver) {
if (prop !== 'code') {
return Reflect.get(target, prop, receiver);
}
if (!receiver.read) {
receiver.read = true;
return target[prop];
}
return null;
}
};
const promo = { code: 'one-time free beer' };
const proxiedObj = new Proxy(promo, discardAfterUsedHandler);
console.log(`You are getting a ${proxiedObj.code}`);
console.log(`You are getting a ${proxiedObj.code}`);
In conclusion, if you need a straightforward solution, closures work well. For a more reusable and structured approach, a class is ideal. When handling multiple properties dynamically, proxies offer the most flexibility.