Control variables and method created while a program is running

Another weird question ! lol

In java, it is impossible to create a variable or a method outside the class declaration.

But in Javascript, it is quite possible. I just spent a lot of time looking for the origin of a very strange bug and ended up realizing… that I had misspelled the name of a variable somewhere in my code (apart from a class declaration), which, instead of updating this variable, was creating a new one with the wrong name in the concerned classes.

So, I wonder if there is a way in Javascript to detect the creation of new variables (or methods) in a class during the operation of the program and how to do it?

Maybe we could list the methods and variables present in the constructor and check during the execution of the program that there are no new ones?

something which could be applied in all constructors and be undone (when debugging is finished)

Is it possible?
What do you think about it?

:slight_smile:

In Java requires all variables have to be declared before assigning a value to it so in Java we have

int n; // declaration
n = 42;// assignment 

in JavaScript we don’t need to declare the variable so

n = 42;

is perfectly acceptable and will assign the value 42 to the variable n. If the variable doesn’t exist then it will create it.

It is a similar when creating classes if a method has the statement

this.n = 42;

will create a class field n even if it doesn’t exist.

So a few typos in naming your variables can create a multitude of logical errors that will melt the brain trying to fix.

A logical error is one that

  • does not generate a compiler / interpreter warning, or
  • does not causing the program to halt unexpectedly

Logical errors are the hardest to find and fix :frowning:

The solution is not to find the unexpectedly created variables at runtime but prevent them in the first place.

You might try strict mode to help prevent this.

For large projects that make extensive use of classes I would recommend Typescript, I used this when creating the canvasGUI library.

1 Like

Inside a class block, all code is already in β€œstrict mode”:

Meaning all variables have to be declared before usage.

Moreover inside a class, we can’t declare global variables anyways.
Therefore, if we mistype a global variable name inside a class, the code should just crash!

For regular JS files, always place β€œuse strict” as their 1st statement, so as to force variables to be declared 1st.

1 Like

(post deleted by author)

thanks.

I use strict mode already everywhere.

I had to modify the value of an existing variable in a series of instances of a class (so outside the declaration of the class) . By making a mistake in the name of the variable, I think I created a new variable in these classes instead of modifying its value.

Sorry,

I realize that I wrote my message too quickly and that I was not very clear!

yes, I took 2 hours to realize that I had forgotten a letter in the name of this variable!

Reflecting, I tell myself that the solution to this problem would be to declare more variables as private (marked with a _ now and preceded by a #later cf Classes - JavaScript | MDN ) because if we make a mistake in writing a function (a β€œget” or a β€œset”) we will inevitably have an error.

… but it makes class writing more cumbersome!

I believe you’re confusing variables w/ object properties (A.K.A. fields).

Strict mode only forces variables to be declared 1st; but no effect on object properties!
For those, you can call Object.preventExtensions() over each object you instantiate:

Let’s say you have this JS class:

class MyClass {
  deer = 10;
  plum = 20;
}

Then you instantiate it and attempt to change its properties, accidentally mistyping their names:

const instance = new MyClass;

instance.dear = 100; // "dear" instead of "deer"
instance.plumb = 200; // "plumb" instead of just "plum"

console.table(instance);

You might expect this output:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (idx) β”‚ Values β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ deer  β”‚ 100    β”‚
β”‚ plum  β”‚ 200    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜

But you end up w/ this 1 instead:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (idx) β”‚ Values β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ deer  β”‚ 10     β”‚
β”‚ plum  β”‚ 20     β”‚
β”‚ dear  β”‚ 100    β”‚
β”‚ plumb β”‚ 200    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”˜

B/c of 1 wrong letter for each those properties, 2 new properties got created rather than updating the values of the original properties!

Now, if we apply Object.preventExtensions() over instances of class MyClass, the code would crash immediately rather than continue on unintentional behavior:

"use strict";

class MyClass {
  deer = 10;
  plum = 20;
}

const instance = Object.preventExtensions(new MyClass);

instance.deer = -10; // ALLOWED! Property name correctly typed!

instance.dear = 100; // CRASHES right here!!! No new props allowed!
instance.plumb = 200; // Same for "plumb" prop.

console.table(instance);

P.S.: Seems like it won’t crash if code is pasted in a browser console or in the NodeJS REPL.
Instead, any new properties would be silently ignored!
However, it will still crash on both Deno & Bun REPL though!

1 Like

If you decide to use Object.preventExtensions() as a way to protect yourself from accidentally add new properties on-the-fly for instances of your classes, it’s way better to call that right inside the constructor() as its last statement rather than repeating it for each single instantiation:

"use strict";

class MyClass {
  deer = 10;
  plum = 20;

  constructor() {
    // Add any extra class properties below before sealing the instance!

    // ...

    Object.preventExtensions(this); // No new props are allowed from now on!
  }
}

const instance = new MyClass; // Instances are already non-extensible!

instance.deer = -10; // ALLOWED! Property name correctly typed!

instance.dear = 100; // CRASHES right here!!! No new props allowed!
instance.plumb = 200; // Same for mistyped "plumb" prop.

console.table(instance);
1 Like

Thank you very much!!!
I was not aware of this possibility at all, I am really happy that it exists (Javascript is increasing in my esteem ->I’m joking<-)

Yes but… :woozy_face:

How does it work in classes that inherit from other classes?
What I mean is that since we often invoke the parent class constructor (with super() ) in the first instruction of the new constructor, can we add new fields since Object.preventExtensions(this); is used on the last line of the parent class constructor?

class A {
   constructor() {
      (...);
      Object.preventExtensions(this);
   }
...
}

class B extends A {
   constructor() {
      super();
      ...
      this.NewField = something; // is there an error here ? I guess there is !!! 
                                 // So, I'm stuck again !!! 
      ...
      
      Object.preventExtensions(this); // does it work here ?
   }
}

:face_with_spiral_eyes:

P.S. I’m going to try tomorrow ( I must really go to sleep now !)

P.S. I suppose I’m going to try something with an if / the actual class of the instance…

Uh no, there’s no confusion about that, it’s just a question of vocabulary. In French, we also talk about variables within classes or to be more precise, about instances variables (as we speak more of methods rather than properties for functions within classes, with Java at least). As these are the same words in French and English (variables, mΓ©thodes, propriΓ©tΓ©s : properties…) I tended to talk about variables (in classes too).
So, I have noted that I should rather talk about properties, fields, or attributes according to my research (mainly there : Object-Oriented Programming Glossary: Key Terms Explained)

I don’t have the opportunity to speak in English with other people : just to read, and see movies in English. Inevitably, I am not absolutely aware of all the nuances of English which for me is a foreign language.

Above all, don’t hesitate to give me details, I really like having the opportunity to improve.

:grin:

Well, it’s foreign to me too, b/c my mother tongue is Portuguese.

Indeed in general abstract terms, object properties are variables, methods are functions and classes are constructor functions.

But the way they’re created and how they interact w/ their object demand a more pedantic terminology.

In JS, there are at least 6 keywords which can be used to declare a variable:

  • var, let, const, function, class, import.

However, none of them can be used to create an object property!

Instead, the default way for it is using the dot . or the brackets [] operator followed by the equal = operator:

  • instance.deer = "animal"; // creating or updating prop. "deer"
  • instance["plum"] = "fruit"; // creating or updating prop. "plum"

If a property doesn’t exist in the object yet, it’s created; otherwise, its value is updated.

It’s pretty much like a dictionary or hashmap container, where each value is associated to a key string name.

BtW, the JSON file format is based on JS objects w/ their { key property: value } pair!

Same applies to methods; which are functions that act upon the properties/fields of an object.

In your 1st post, b/c it didn’t have an example code to infer from, I had to rely on your words only and, naΓ―vely thought you were talking about regular variables.

Only on your follow-up I’ve finally figured out it was all about object properties instead!

You’re gonna need to delegate to the β€œparent” class the responsibility to add properties of its subclasses as 1 of its constructor()'s parameters.

Before using Object.preventExtensions(this);, the β€œparent” class needs to call Object.assign() in order to append properties coming from its β€œchildren”, b/c they can’t add properties by themselves due to preventExtensions():

"use strict";

class A {
  constructor(subclassProps) {
    // (...);

    // Append subclass properties to this parent class instance:
    subclassProps && Object.assign(this, subclassProps);

    // Now seal the new created instance so no more props. can be added anymore:
    Object.preventExtensions(this);
  }
}

class B extends A {
  constructor(animal, food) {
    // Call parent class passing an object containing this subclass own props:
    super({ animal, food });

    // (...);
  }
}

// Arguments "penguin" & "fish" will become B's properties "animal" & "food"
// respectively; even though parent class A is responsible to append them:
const b = new B("penguin", "fish");

console.table(b);

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ (idx)  β”‚ Values    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ animal β”‚ "penguin" β”‚
β”‚ food   β”‚ "fish"    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜