60

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

Community
  • 1
  • 1
danronmoon
  • 3,814
  • 5
  • 34
  • 56

6 Answers6

78

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.

ziad-saab
  • 19,139
  • 3
  • 36
  • 31
  • Aren't objects passed by reference? – danronmoon Jun 01 '12 at 02:17
  • Yes, but `const obj = {}` is kinda useless because the reference will be constant, but you can still modify the object. – ziad-saab Jun 01 '12 at 02:18
  • 8
    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](http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing). Fun fact: *the ECMAScript specification does not use the term "reference"*. –  Jun 01 '12 at 02:20
  • My answer still holds though because you won't be able to modify that non-writable property – ziad-saab Jun 01 '12 at 02:22
  • @zi42 Sorry, didn't mean to hijack :-) –  Jun 01 '12 at 02:22
  • 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. – danronmoon Jun 01 '12 at 02:23
  • @pst not at all, thanks for making the very important nuance. We're all here to learn. – ziad-saab Jun 01 '12 at 02:24
  • Does anyone know why it is the way it is? What is so wrong with using const on object props? – danronmoon Jun 01 '12 at 02:27
  • 5
    @pst—the term "reference" most definitely **is** used: the [reference type](http://es5.github.com/#x8.7) 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". – RobG Jun 01 '12 at 02:47
  • @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](http://www.ecmascript.org/community.php). – RobG Jun 01 '12 at 02:52
  • @RobG 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). –  Jun 01 '12 at 03:18
  • 1
    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. – Lonnie Best Sep 22 '18 at 09:26
12
const person = {
    name: "Nicholas"
};

// works
person.name = "Greg";



console.log(person) //Greg 

That's why use Object.defineProperty

zloctb
  • 10,592
  • 8
  • 70
  • 89
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.

p0wdr.com
  • 307
  • 2
  • 4
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

Bằng
  • 557
  • 6
  • 24
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
danronmoon
  • 3,814
  • 5
  • 34
  • 56
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

Volomike
  • 23,743
  • 21
  • 113
  • 209