You'll really have to look into implementation details for a complete answer. But to start there's V8's runtime-strings.cc and builtins-string-gen.cc. It's a deep dive--and I don't know c++, so I'm not entirely sure if I'm even looking at the right files, but they seem to use different approaches depending on the size of the needle and the depth of recursion needed to build a search tree.
For example, in builtins-string-gen.cc there's a block under ES6 #sec-string.prototype.replace
that checks if the search_string
has a length of 1, and if the subject_string
length is greater than 255 (0xFF
). When those conditions are true it looks like Runtime_StringReplaceOneCharWithString
in runtime-strings.cc is called which in turn will try calling StringReplaceOneCharWithString
first with a tree-traversable subject_string
.
If that search hits the recursion limit, the runtime makes another call to StringReplaceOneCharWithString
but this time with a flattened subject_string
.
So, my partially educated guess here is you're always looking at some kind of linear time. Possibly O(mn) when hitting the recursion limit and doing a follow-on naive search. I don't know for sure that it's a naive search, but a flattened string to me implies traversing the subject_string
step-by-step instead of through a search tree.
And possibly something less than O(mn) when the tree-traversal doesn't hit the recursion limit, though I'm not entirely sure what they're gaining by walking the subject_string
recursively.
For an actual what is the time complexity for Javascript implementations, you'll probably want to ask the runtime devs directly or see if what they're doing is like other string searching algorithms to figure out which cases run in what time complexity.