Print a string of characters with their occurrences
Big \$O\$
Time complexity is a ratio of some input metric (e.g. the number of character in the string) to the number of instructions required to complete the function.
In this case the metric \$n\$ is the string length. The first loop that uses String.match must for each character check all characters to find a count. That means at least \$n * n\$ steps need to be performed to do the operation.
Thus the complexity of the function is said to be \$O(n^2)\$
If you think about how you would solve it on paper. You would go over each character once with a list of characters found adding 1 to each count as you find them. This would have a time complexity of \$O(n)\$
Maps use a hash function to locate an item \$O(1)\$. So the time complexity to find out if you have counted a character before is \$O(1)\$, rather than your regExp \$O(n)\$
function countLetters(str) {
    const charCounts = {};
    var result = &quot;&quot;;
    for (const c of str) { 
        if (charCounts[c]) { charCounts[c] += 1 }
        else { charCounts[c] = 1 }
    }
    for (const [char, count] of Object.entries(charCounts)) {
        result += char + &quot; has &quot; + count + &quot; &quot;;
    }
    return result;
}

The second loop to create the result, will count in the worst case each character again. Thus the number of instructions is \$2n\$ In big \$O\$ notation the scale \$2\$ is insignificant compared to powers, even if it was \$1000000n\$ we ignore the scale and make it just \$n\$
It can not be done with less complexity as you need to check every character at least once. Because you do not know what the characters are before you check them.
Style Notes.

Use ; or not, never use them sometimes.
Careful with indentation. You indent 4 and sometime 2 spaces, use either not both.
Use Function declarations in favor of arrow functions when in global scope.
Variables that do not change should be declared as constants const
Use for of rather than  for in