Early ES5 emulations of const often have critical flaws: attaching constants to the global window object makes function-declared constants accessible outside their enclosing scope, violating lexical scoping rules. Additionally, these implementations may fail to enforce immutability or prevent deletion. Below are accurate emulations of let and const using ES5 features that align with modern JavaScript behavior.
Emulating let
let declares block-scoped variables, which can be simulated in ES5 using Immediately Invoked Function Expressions (IIFEs) to create isolated scopes. For example, fixing scoping in a loop:
for (var count = 0; count < 10; count++) {
(function(localCount) {
console.log(localCount);
})(count);
}
Each iteration passes the current loop value to an IIFE, which captures a copy of the variable in its own scope—mimicking the block scoping of let.
Emulating const
A const declaration creates an immutable, lexically scoped constant with the following properties:
- The value cannot be modified after enitialization.
- Constants are only accessible within their declared scope (function or block).
- The
deleteoperator cannot remove the constant. - Constants are not enumerable in their containing context.
Here’s an ES5 implementation that enforces all these rules:
var emulateConst = function emulateConst(key, value) {
Object.defineProperty(this, key, {
enumerable: false,
configurable: false,
get: function() {
return value;
},
set: function() {
throw new TypeError('Assignment to constant variable.');
}
});
};
This uses Object.defineProperty to define a property on the current execution context (global or function scope) with strict constraints:
enumerable: falseprevents the constant from appearing in enumerations likefor...inloops.configurable: falseblocks deletion via thedeleteoperator.- A getter returns the initial value, and a setter throws an error if reassignment is attempted.
Usage in Global Scope
emulateConst('MAX_RETRIES', 5);
console.log(MAX_RETRIES); // Output: 5
delete MAX_RETRIES; // Fails to remove the constant
console.log(MAX_RETRIES); // Output: 5
MAX_RETRIES = 10; // Uncaught TypeError: Assignment to constant variable.
Usage in Function Scope
function processOrder() {
emulateConst('SHIPPING_FEE', 4.99);
console.log(SHIPPING_FEE); // Output: 4.99
delete SHIPPING_FEE; // No effect
console.log(SHIPPING_FEE); // Output: 4.99
SHIPPING_FEE = 6.99; // Uncaught TypeError: Assignment to constant variable.
}
processOrder();
console.log(SHIPPING_FEE); // ReferenceError: SHIPPING_FEE is not defined