How to create Javascript constants as properties of objects using const keyword?
Asked Answered
A

6

61

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

Angadreme answered 1/6, 2012 at 2:9 Comment(0)
N
80

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.

Nationalist answered 1/6, 2012 at 2:14 Comment(12)
Aren't objects passed by reference?Angadreme
Yes, but const obj = {} is kinda useless because the reference will be constant, but you can still modify the object.Nationalist
No. Objects are not "passed by reference" (this is an ambiguous phrase at best). An implementation will generally "pass by value of the reference" but the important thing is JavaScript has Call By Object Sharing semantics. Fun fact: the ECMAScript specification does not use the term "reference".Aldosterone
My answer still holds though because you won't be able to modify that non-writable propertyNationalist
@zi42 Sorry, didn't mean to hijack :-)Aldosterone
So const really isn't a win for us at all... unless you like to make a lot of procedural code and plop constants directly on a function's activation object.Angadreme
@pst not at all, thanks for making the very important nuance. We're all here to learn.Nationalist
Does anyone know why it is the way it is? What is so wrong with using const on object props?Angadreme
@pst—the term "reference" most definitely is used: the reference type is a specification artifact to explain (among other things) how assignment of values works. There is even the statement "function calls are permitted to return references".Merman
@moonDogDog—probably because object properties and environment variables are fundamentally different things. const is for variables, writeable is for object properties. Other than that, you'll need to ask the authors of ES5, there's a community.Merman
@Merman Whoops, my memory had blanked that out! Thanks for the correction. Note that the "reference type" in that aspect is not related to "passing objects", though (which is where I was fixated).Aldosterone
If you try to change its value, it won't change, but you won't get an error either! obj.MY_FAKE_CONSTANT = "something else"; // no error.Ginni
L
13
const person = {
    name: "Nicholas"
};

// works
person.name = "Greg";



console.log(person) //Greg 

That's why use Object.defineProperty

Linder answered 2/8, 2016 at 3:19 Comment(0)
D
10

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.

Deficient answered 15/7, 2017 at 10:10 Comment(0)
C
8
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

Chaudoin answered 9/6, 2018 at 15:58 Comment(1)
Or simply omit configurable entirely, since the default is false.Intonate
B
4

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
Bucksaw answered 4/8, 2016 at 16:39 Comment(0)
I
2

I thinks that you should define a constant in property making configurable:false and writable:false in Object.defineProperty, if you leave configurable:true then you can still making changes to the property

for example: when you set configurable:true in Object.defineProperty

const obj = {name: 'some name'};
Object.defineProperty(obj, 'name', {
  enumerable: true,
  configurable: true,
  writable: false
});

With this you are making obj.name 'constant' but after you could do

Object.defineProperty(obj, 'name', {
  enumerable: true,
  configurable: true,
  writable: true // <-- you can set to true because configurable is true
});

But if you set configurable to false then you could never edit writable property.

Object.defineProperty(obj, 'name', {
  enumerable: true,
  configurable: false, // <-- with this you never could edit writable property
  writable: false
});

And for all properties you could use Object.freeze

Object.freeze(obj);

Object.freeze will iterate over enumerable properties only

Intrigant answered 5/3, 2019 at 23:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.