somehow I need to update all the functions so that I can get how many times each function is called during the program lifecycle

I have a square function for example in my system like

function square(a) {
  console.log(a);
  return a * a;
}

This square function is called many times during the lifecycle of the program like

square(5);
square(1);
square(2);
square(3);

Now the problem is that I want to get the count of the times square function is called at any given time like square.count should return 4

The solution should work not only with the square function but should also be scalable and be applicable to other functions available in the system. For e.g, if I have a power(a,n) function as well then I should be able to get the count for power function by something like power.count


You can create a function that wraps a target function and records that information, like this:

function addCounter(fn) {
    // Create a wrapper function
    const newfn = function(...args) {
        // In the wrapper, increment the count
        ++newfn.count;
        // Call original and return its return value
        return fn.apply(this, args);
    };
    // Set up the count
    newfn.count = 0;
    // Ensure the new function's `length` matches the original
    // (in case you have anything relying on it)
    Object.defineProperty(newfn, "length", {
        value: fn.length,
        configurable: true,
    });
    // Return the new function
    return newfn;
}

Then you replace the function like this:

square = addCounter(square);

Now whenever you call it, the counter gets incremented.

Live Example:

function addCounter(fn) {
    // Create a wrapper function
    const newfn = function(...args) {
        // In the wrapper, increment the count
        ++newfn.count;
        // Call original and return its return value
        return fn.apply(this, args);
    };
    // Set up the count
    newfn.count = 0;
    // Ensure the new function's `length` matches the original
    // (in case you have anything relying on it)
    Object.defineProperty(newfn, "length", {
        value: fn.length,
        configurable: true,
    });
    // Return the new function
    return newfn;
}

function square(a) {
    console.log(a);
    return a * a;
}

square = addCounter(square);
console.log(square(5));
console.log(`square.count = ${square.count}`);
console.log(square(1));
console.log(`square.count = ${square.count}`);
console.log(square(2));
console.log(`square.count = ${square.count}`);
console.log(square(3));
console.log(`square.count = ${square.count}`);
.as-console-wrapper {
    max-height: 100% !important;
}

You might not bother with the length bit, it's unusual for code to look at the length on functions, but it's there to be thorough. (You have to use defineProperty to set length. It's a read-only property, but it's configurable, so we can redefine it.)

Just for completeness: If anything has grabbed its own reference to that function before you update it, then calls through that other reference won't be counted. But that's unusual, hopefully you don't happen to have code doing it. One place you could run into that is if you're using CommonJS modules (either directly, or because your bundler converts JavaScript standard module syntax to CommonJS instead, as some do) and you export it before you replace it (which seems fairly unlikely). The reason that would be a problem is that with CommonJS modules, what's exported is a copy of the function reference. It's not a problem with standard JavaScript modules (import/export) used natively, because imports are live bindings to the original export, so the above would work just fine with it.