In JSTS there is a bit more decoupling out of necessity. In the source there are *no* operations on the Geometry base class. This was actually a problematic circular dependency (more on that later). Instead operations are "monkeypatched" in the single file build with
https://github.com/bjornharrtell/jsts/blob/master/src/org/locationtech/jts/monkey.js. When using JSTS via individual ES modules there are no operations on Geometry and you will have to use the operations via eg. BufferOp for example and this can make the total build smaller. This is one reason JSTS still requires some patching of JTS before transpilation - in this particular case it's about
https://github.com/locationtech/jts/pull/200. There are other needed but unrelated patches too, for example
https://github.com/locationtech/jts/pull/222.
Agreed dynamic linking could be used to bring in parts dynamically as needed, but such mechanisms are poorly standardized in JavaScript. An old approach was require.js / AMD modules which got some traction but has its drawbacks. ES module import actually has two variants, one static (keyword) and one dynamic (function) (1). The dynamic import function is still a stage 3 proposal (2) (even though Chrome has implemented it).
The static ES module keyword import is actually even more strict than Java import (AFAIK Java is more lenient towards circular dependencies as it can resolve them using multiple passes during compilation), so it has the same drawbacks (and benefits).
The popular term of trying to strike a balance between static and dynamic linking is called code splitting and is somewhat supported in bundlers like rollup and webpack. To me none of the available solutions seem very practical in a general sense, but might make sense for a large scale single application.