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