4

I have registered some javascript functions in the global scope:

function Test1() {}
function Test2() {}

Now I want to run all javascript functions whose name starts with 'Test', how to do it?

I want to prevent saving each functions into a variable because it does not scale. Nor to push them to a queue and execute them later, since people need to remember adding their functions to the queue when they write the test and I don't want that.

yijiem
  • 359
  • 2
  • 17
  • are the functions named "Test" + number or "Test" + anything else ? – Stakvino Sep 24 '18 at 22:49
  • 1
    Iterate over all properties of `window` and check whether it starts with `Test`? But since there are *lots of things* in global scope, maintaining your own list is probably better. Naming variables/functions with consecutive numbers is a string sign that you should be using an array instead. *"I want to prevent saving each functions into a variable because it does not scale."* Can you elaborate what you mean? – Felix Kling Sep 24 '18 at 22:50
  • `I want to prevent saving each functions into a variable because it does not scale.` Can you elaborate on this, because it doesn't sound right. – Matt Way Sep 24 '18 at 22:53
  • If you don't provide more/other information then this is a duplicate of [Calling multiple functions with names matching Regex](https://stackoverflow.com/q/44099512/218196) – Felix Kling Sep 24 '18 at 22:53
  • Also related: [“Variable” variables in Javascript?](https://stackoverflow.com/questions/5187530/variable-variables-in-javascript/5187652) – Felix Kling Sep 24 '18 at 22:58
  • The function will actually named as Test, but I thought Test carries the same idea. – yijiem Sep 25 '18 at 01:12
  • Ideally, I will create a test runner in javascript that will run all global tests which end or start with "Test" (depends on how I actually design it). Other people can add test cases, but they just need to add another function for their test case and not worry about the execution (somewhat like writing gtest). – yijiem Sep 25 '18 at 01:17

3 Answers3

6

var globalKeys = Object.keys(window);

for(var i = 0; i < globalKeys.length; i++){
  var globalKey = globalKeys[i];
  if(globalKey.includes("Test") && typeof window[globalKey] == "function"){
    window[globalKey]();
  }
}
Stakvino
  • 642
  • 4
  • 9
  • that's two loops, an ```if`(globalKey.includes ...) window[globalKey]()``` in first (and only) loop would be way better imho – Loïc Sep 24 '18 at 23:04
  • Thanks Stakvino, this looks clean. I thought about searching all objects under global scope. I am very new to javascript and surprised to see there is no better way of doing it. – yijiem Sep 25 '18 at 01:38
  • @Loïc true you can do it in one loop i did the modification. yijiem : yes unfortunately there is no straight froward way to do it in Javascript – Stakvino Sep 25 '18 at 11:42
  • I actually prefer the first version with 2 loops, it looks cleaner than this one. This one might be preferred if we have a performance issue and want to optimize to extreme in real production. – yijiem Sep 25 '18 at 14:46
  • @yijiem Exactly – Stakvino Sep 25 '18 at 14:50
  • it's not about optimizing to extreme, it's about having a minimal concern about performance. This is client code that may be running on lots of clients, if you are going to code please do it cleverly. Yes you can always scale horizontally, but better is to code properly. https://en.wikipedia.org/wiki/Programming_complexity – Loïc Sep 25 '18 at 22:28
2
function Test() { console.log('test') }
Object.keys(window).filter(s => s.startsWith('Test')) // [ "Test" ]

As you can see, functions are defined on the global scope.

const isTest = s => typeof s === 'function' && s.startsWith('Test')
Object.keys(window).filter(isTest).map(t => t())

I don't know your use case entirely, but I suspect it would be better to provide your own object.

const tests = {}
tests.Test1 = () => {/*...*/}
Benny Powers
  • 5,398
  • 4
  • 32
  • 55
  • I don't understand the last part. My use case is that I will have a piece of javascript code that executes all test functions (like the 1st and 2nd part of your answer) and others can add their own test cases as separate functions and not worry about the execution. – yijiem Sep 25 '18 at 01:43
  • It seems to me that you are combining two practises which are both sub-optimal: 1. defining global variables, 2. relying on naming convention to call functions. It would be better to simply define all functions to be called as properties of some object, that way test-case authors could write their functions however they wanted, and know that they'd be called, because all functions that are children of `tests` will be called. – Benny Powers Sep 25 '18 at 06:26
  • Good idea! This also does not put too much overhead on test author (they just need to remember to add their test case to the global test object). – yijiem Sep 25 '18 at 14:56
0

You could get all elements of window and check them. Here is a basic starting point :

for(let objectName in window){ console.log(`${objectName} : ${typeof window[objectName]}`) }

Now objectName is actually a string, it could be anything, just check if it starts with Test, and if its type is a function.

Loïc
  • 11,804
  • 1
  • 31
  • 49
  • 1
    You can (and should) use `window[object]` instead of `eval(...)`. `eval('window.'+object)` will fail if `object` is `"foo bar"`. – Felix Kling Sep 24 '18 at 22:58