[SOLVED] Can I check if a const exists in JS before declaring it if it does not?

Richard Headkid
18 Dec 2020, 12:44

The title speaks for itself.

I want to do something like:

if (!log) {
  const log = console.log
}

OR

const log = typeof(log) === 'undefined' ? console.log : ''

I know the first one won't work because I couldn't declare a const in the if block even if log wasn't already declared, but I really wish I could find a way to do this without: image


The Pixie
18 Dec 2020, 13:32

The second is the right approach, but I suggest:

if (typeof(log) === 'undefined') const log = console.log

But log should be defined as per an update earlier today.


Richard Headkid
18 Dec 2020, 14:22

The second is the right approach, but I suggest [ . . . ]

Cool, cool.

log should be defined as per an update earlier today

Awesome!

I was really just using that one as an example while trying to learn how to handle that situtation. I thought we couldn't declare in an if block, but I was obviously doing something else wrong in my code.

Thanks!


Richard Headkid
18 Dec 2020, 14:32

UPDATE

if (typeof(testRH) === 'undefined'){ const testRH = "Richard Headkid"}

That doesn't work if testRH does not exist. (It doesn't throw an error either way, though.)


image


Richard Headkid
18 Dec 2020, 14:39

EDIT

This is not it!

See this post for the correct answer.


Got it!

if (typeof(RH) === 'undefined'){ window["RH"] = "Richard Headkid"}

image


mrangel
18 Dec 2020, 15:20

I know the first one won't work because I couldn't declare a const in the if block even if log wasn't already declared

Well, you could. But it's scope would be within the if block; which isn't particularly useful to you. const, like let, is scoped within the innermost enclosing {block}.

The simplest way to do this would simply be to make it a variable.

if (typeof(RH) === 'undefined'){ RH = "Richard Headkid"}

Works fine. It isn't a const, but then neither is your solution.

If you really want it to be a constant, you could do something like:

if (typeof(RH) === 'undefined'){ Object.defineProperty(this, "RH", {value: "Richard Headkid", writable: false}); }

but that would be very silly and possibly confusing, as it doesn't prevent you from accidentally declaring a variable with the same name as the property.


The Pixie
18 Dec 2020, 15:27

Thinking about it...

if (typeof(testRH) === 'undefined'){ const testRH = "Richard Headkid"}

This will create a constant, testRH, that exists only inside the curly braces, so not a lot of use.


Richard Headkid
18 Dec 2020, 15:35

Ha.

I checked if it existed, but not if it could be modified.

This is the solution:

if (typeof(RH) === 'undefined'){ Object.defineProperty(this, "RH", {value: "Richard Headkid", writable: false}); }

image


Whoo-hoo!

Thanks, guys!


mrangel
18 Dec 2020, 17:41

Wow! Object.defineProperty is useful for more than just tracking changes :p

(Thinking about it… if I was trying to make a library that would me to convert an old .aslx file to work with this new engine, how would be best to implement changescripts? Would I need to abuse defineProperty excessively, or is there some system already implemented that would take care of it?)


The Pixie
18 Dec 2020, 18:03

There is nothing currently implemented like that. I was not aware Object.defineProperty could be used like that.


Richard Headkid
18 Dec 2020, 18:16

how would be best to implement changescripts?

Pixie says:

https://github.com/ThePix/QuestJS/wiki/Outstanding-Features#change-scripts


EDIT

Oh. Pixie actually said something in response to that before my page refreshed. (Hehehe.)


The Pixie
18 Dec 2020, 19:28

If something could be done that would be great because, as mrangel says, it would help when translating from 5 to 6. However, it would be a fairly fundamental change at this stage, with a lot of changes across the whole system.


Richard Headkid
18 Dec 2020, 19:35

I poked around at it for a minute, but I couldn't get anything but errors.

I wonder how using Object.defineProperty for a change script on something like game.player.hitpoints would work . . .

I read the Mozilla page. I sort of understand setters and getters (I think), until they start criss-crossing them and doing crazy stuff. That site tends to go from 0 to 50, leaving me on the side of the road.


mrangel
18 Dec 2020, 20:35

I'd probably do something like:

function enableChangescripts (obj, attr, script=null) {
  // don't define the property twice
  if (!obj.hasOwnProperty("_real_"+attr)) {
    // preserve the existing value of the attribute if it has one
    // and make this property non-enumerable so it doesn't show up in `foreach`
    Object.defineProperty(obj, "_real_"+attr, {value: (obj.hasOwnProperty(attr) ? obj[attr] : undefined), writable: true, enumerable: false});
    Object.defineProperty(obj, attr,  {
      get: function  () {
        return (this["_real_"+attr]);
      },
      set: function (val) {
        var oldvalue = this["_real_"+attr];
        this["_real_"+attr] = val;
        if (typeof this["changed"+attr] === 'function') {
          this["changed"+attr].call(this, oldvalue);
        }
      }
    });
  }
  if (typeof script === 'function') {
    this["changed"+attr] = script;
  }
}

(simplest construction I can see to make it work like it does in 5.8; takes an object reference, an attribute name, and optionally a changescript. Then makes an underlying "real" property that would be something like game.player._real_hitpoints. You can bypass the changescripts by setting the real property directly. The real property doesn't show up if you foreach over the object or use .keys()/.values() because it isn't enumerable; but you can find it using Object.getOwnPropertyNames(obj) for the purposes of loading/saving)