How to create Javascript constants as properties of objects using const keyword?
How come constants cannot be set as properties of objects which are variables themselves?
const a = 'constant' // all is well
// set constant property of variable object
const window.b = 'constant' // throws Exception
// OR
var App = {}; // want to be able to extend
const App.goldenRatio= 1.6180339887 // throws Exception
And how come constants passed by reference suddenly become variable? EDIT: I know App won't (or rather... SHOULDN'T) be mutable; this is just an observation...
(function() {
const App;
// bunch of code
window.com_namespace = App;
}());
window.com_namespace; // App
window.com_namespace = 'something else';
window.com_namespace; // 'something else'
How can a nicely organized, extensible, object-oriented, singly namespaced library containing constants be made with these limitations?
EDIT: I believe zi42, but I just have to ask why
Solution 1:
You cannot do it with constants. The only possible way to do something that behaves like you want, but is not using constants, is to define a non-writable property:
var obj = {};
Object.defineProperty( obj, "MY_FAKE_CONSTANT", {
value: "MY_FAKE_CONSTANT_VALUE",
writable: false,
enumerable: true,
configurable: true
});
Regarding your question as to why a const
passed to a function becomes variable, the answer is because it's passed by value and not by reference. The function is getting a new variable that has the same value as your constant.
edit: thanks to @pst for noting that objects literals in javascript are not actually "passed by reference", but using call-by-sharing:
Although this term has widespread usage in the Python community, identical semantics in other languages such as Java and Visual Basic are often described as call-by-value, where the value is implied to be a reference to the object.
Solution 2:
const person = {
name: "Nicholas"
};
// works
person.name = "Greg";
console.log(person) //Greg
That's why use Object.defineProperty
Solution 3:
There is a far simpler way to do this. I like this pattern. Simple Objects.
window.Thingy = (function() {
const staticthing = "immutable";
function Thingy() {
let privateStuff = "something";
function get() {
return privateStuff;
}
function set(_) {
privateStuff = _;
}
return Object.freeze({
get,
set,
staticthing
});
}
Thingy.staticthing = staticthing;
return Object.freeze(Thingy);
})();
let myThingy = new Thingy();
Thingy.staticthing = "fluid";
myThingy.staticthing = "fluid";
console.log(Thingy.staticthing); // "immutable"
console.log(myThingy.staticthing); // "immutable"
Object.freeze is doing the work here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
if you want you can leave the static property off the instance by leaving it off the object literal return on the constructor function.
const will only make it a read-only reference. As soon as you assign it, like here in a object literal it becomes a property of the constructed object.
Solution 4:
var obj = {};
Object.defineProperty( obj, "MY_FAKE_CONSTANT", {
value: "MY_FAKE_CONSTANT_VALUE",
writable: false,
enumerable: true,
configurable: false // instead of true
});
We should set also configurable to be false so that it will prevent the property from being deleted from the obj
delete obj.MY_FAKE_CONSTANT;
With configurable to be true, after the line, we don't have the MY_FAKE_CONSTANT anymore.
Reference
Solution 5:
You should not forgot that the const declaration "creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned"
The const keyword work in a similar way than 'let', so you can redeclare it in an other block
const MyConst = 5;
console.log('global MyConst =', MyConst); //global MyConst = 5
if(true){
const MyConst = 99
console.log('in if block, MyConst =', MyConst); //in if block, MyConst = 99
}
console.log('global MyConst still 5 ?', MyConst===5); //global MyConst still 5 ? true
Just like @ziad-saab mantioned if you want an object property than act like a constant, you have to define it as a non-writable property.
if your constant is an object and is property should not change, use Object.freeze() to make the object immutable.
(function(){
var App = { };
// create a "constant" object property for App
Object.defineProperty(App , "fixedStuff", {
value: Object.freeze({ prop:6 }),
writable: false,
enumerable: true,
configurable: true
});
Object.defineProperty(window, "com_namespace", {
value: App,
writable: false,
enumerable: true,
configurable: true
});
})()
com_namespace.newStuff = 'An extension';
com_namespace.fixedStuff.prop = 'new value'; // do nothing!
console.log(com_namespace.fixedStuff.prop); //6