A modern (high-performance) JavaScript engine is unlikely to allocate a heap object to run a single method, although the originals may well have done so. Depending on allocation and garbage collection strategies, that could be close to free anyway; if nothing else you can pre-allocate a single Number, String, and Boolean (and now BigInt) and swap the value inside of them out when you need to use it, since there's only ever one of these methods running at a time. The overhead doesn't need to be significant, and from the design perspective of letting the program keep running instead of crashing whenever possible it's a very JavaScript thing to do anyway.
A language like Python, on the other hand, keeps all of its primitives in objects, so there’s no extra step.
We could paraphrase this as "Python is slow all the time, instead of only sometimes" and it might highlight one issue that could motivate making different choices here. If you expect arithmetic operations to be much more common than toFixed(), doing dynamic dispatch for them starts to sound like a pretty bad idea and occasionally allocating a short-lived object for rare operations sounds pretty good.
This is a caricature, and in practice you're not going to find much of significance between them; probably both avoid doing any of what the semantics would suggest in typical cases. In any case, Python is older than both Java and JavaScript, so it's not that putting everything in objects is a later invention (Smalltalk is from the 70s!).
However Java didn’t handle these primitives the same way.
I think it's worth noting here that Java absolutely does have autoboxing, and it's used every time a primitive value is stored in or retrieved from a list. It's probably more of an overhead than JavaScript's in practice, since these genuinely do have to be real objects and they arise in potential hot loops. The term itself has been transferred from Java to JavaScript, which didn't have a name for this and doesn't call them boxed types.
The question is why do things this way: this is a language design question as much or more than a performance question. JavaScript arose under unusual conditions. In its public form it was meant to resemble Java. It was expected to be used primarily for very small things by people who weren't necessarily programmers before that. It was going to be encountered mostly by people who hadn't chosen to use it and didn't understand what it was, and visible breakage for them was undesirable.
At a lot of points it strives to let code carry on and maybe do the right thing rather than failing and breaking the web page. The "autoboxing" you're talking about isn't really identified as that specifically: it's "the program tried to use a member access on this non-object, so now the ToObject coercion (§9.9) is applied to try to make that work", much like all the implicit coercions where "123" * 2 is 246 or [[![]][+[]]+[]][+[]][+[]] is `"f". On this level, it's taking a program that would have crashed and salvaging it into something like what the programmer must have meant.
It is a choice to have the "boxed" types at all. These do hang a lot of utility functions off them, but it would have been possible to have String.toLowerCase in the same way as Math.sin, or top-level functions like Python's str, and then purely have the primitive types. Actually storing the object forms of these types is quite rare. This would have looked less like Java, meant longer code where they were used, and been further from the Self-influenced OO design, but it's a viable road not taken.
Having only the object types does seem like it would have had performance implications at scale, if they did go through all the dispatch rigamarole and have to allocate a new heap object for x++, but it would be possible to present things that way at the surface and really use tagged pointers or something below that. This would have been practical at the time, and perhaps there's just historical contingency.
Whether or not it actually happens, autoboxing is a good story for how utility operations are made available: letting x.toString() work all the time is good for programming with it, especially for the anticipated users who are not particularly programmers before that. Auto(un)boxing also means syntactic operators can be used for primitives and their equivalent lifted types, but don't need to be more generally exposed, which the object system wasn't terribly prepared for. There are some cases where you can distinguish them (typeof, new Number(1) != new Number(1)), but mostly it acts as though they are simultaneously both objects and primitives, depending on what you do.
The language also came about in ten days; mistakes were made.
thisis not autoboxed, and native built-ins are no longer even specified to autobox primitives (although this is ultimately an implementation detail anyway). $\endgroup$a.toLowerCase()is in factb.toLowerCase.call(a)- the temporary object really isn't kept around for long. $\endgroup$'hello'.toLowerCase()(ora.toLowerCase()foraholding astring-typed value). $\endgroup$