Background: I was on the ECMAScript technical committee in the late 1990s and one of the implementers of Microsoft's JScript engine.
Let me begin by saying what I always say when faced with a "why not?" question: language designers are not required to give good reasons why they did not spend hundreds of millions of other people's dollars on a feature that someone happens to like. Rather, the person pitching the feature is required to give good reasons why that's the best way to spend that time, effort and money. You've made an argument with no numbers attached to it that bytecode would be a cost savings in terms of bandwidth. I would encourage you to work up some actual numbers, and compare that to the costs of creating yet another language; those costs are significant. Remember in your analysis that "implementation" is one of the smallest costs. Also in your analysis include who saves the money vs who spends the money, and you will find that the people spending the money are not the ones saving it; incentives matter.
That said, this is one of the more reasonable "why not?" questions because it is a feature we considered and rejected for reasons.
We considered such a scheme, both within Microsoft and at the TC level; since JScript was already implemented as compiling to a well-designed, principled bytecode language, it would have been straightforward for us to propose it as a standard and we considered doing so.
We decided not to, for a variety of reasons including:
- It would have created an enormous amount of work for browser providers, who were already vexed by the expense of producing an efficient, compliant JS implementation.
- Creating a secure JS implementation that resists attacks by bad actors is hard enough; should we double the surface area available to attack? Probably not.
- Standards are an impediment to innovation. If we discovered that a small change to our bytecode language would make a big difference in some previously-unforeseen or previously-unimportant user scenario, we were free to make that change. If it was a standard, we would not be free to create that user benefit.
But that analysis presupposes that the reason to do the feature at all is performance. Interestingly enough, the customer requests that motivated considering this feature back in the 1990s were not primarily about performance.
Why not? The 1990s was a very different time for JS than today; scripts were mostly tiny. The notion that there would someday be frameworks with hundreds of thousands of lines was not even close to being on our radar. Downloading and parsing JS was a tiny fraction of the time spent downloading and parsing HTML.
Nor was the motivation the extension to other languages, though that was of interest to Microsoft as we had VBScript running in the browser as well, which used a very similar bytecode language. (Being developed by the same team and compiled out of the same sources and all.)
Rather, the primary customer scenario for motivating bytecode in the browser was to make the code harder to read, understand, decompile, reverse-engineer and tamper with. That a bytecode language is hardly any additional work to understand for any attacker with reasonable resources was major points against doing this work; we did not want to create a false sense of security.
Basically there were lots of expenses and precious few benefits, so it did not get done. Something must have changed between 1998 and 2015 that made WebAssembly have a reasonable price-to-benefit; what those factors are, I do not know. You'd have to ask an expert on WebAssembly.
"View Source" was in the beginning, and still is to some extent, considered to be an important feature of the web. It is how generations of web developers learned web development, and the relevant standards bodies (ECMA TC39, W3C, WHATWG) still take it very seriously.
ECMAScript files are typically "minified" before being deployed. This includes removal of all comments, all whitespace, and renaming of all identifiers to be as short as possible, plus some higher-level optimizations such as removal of dead code.
Support for compression exists in HTTP since HTTP/1.0 (early 1996). ECMAScript is text, and text compresses really well. In fact, ECMAScript is text with lots of redundancies (lots of appearances of
for, and so on), and compression algorithms thrive on redundancy. So, the amount of data that is transferred is much smaller than you make it out to be. As an experiment, try compressing an ECMAScript source file with one of the typical compression algorithms used on the web (e.g gzip or deflate), and compare that to the size of the compiled bytecode of the same file.
It turns out that compressed source code is actually pretty small, often comparable or smaller than a typical byte code file.
Also, there are specialized compression algorithms for what I will now term "web text".
Zopfli is an improved encoding algorithm for web text compatible with deflate/zlib. This means it can be decoded by any delate/zlib compliant decoder, in other words, it can be uncompressed by every browser without changes. Compressing takes about 80 times longer than with deflate, for a 3%–8% improvement in output size over "naked" deflate. This might not make sense to do on-the-fly for dynamically created content, but pre-compressing something like JQuery might make sense.
Brotli is a new compression algorithm based on LZ77, Huffman, context modeling, and some other tricks, e.g. a pre-defined dictionary of frequent text chunks extracted from a large corpus of web sites, texts, ECMAScript source files, CSS files, etc. It can achieve up to 25% better compression than deflate/zlib. It is designed to be efficiently decoded on low-end portable devices.
Which brings us to the next problem: there is no standardized bytecode format for ECMAscript. In fact, some implementations may not even use bytecode at all! For example, for the first couple of years, V8 compiled ECMAScript straight to native machine code, with no bytecode step in between. Chakra, SquirrelFish Extreme, and SpiderMonkey all use bytecode, but they use different bytecode. dyn.js, TruffleJS, Nashorn, and Rhine don't use ECMAScript-specific bytecode, they compile to JVML bytecode. Likewise, IronJS compiles to CLI CIL bytecode.
Now, you might say: why not define a standardized bytecode format for ECMAScript? The problems with this are two-fold:
A bytecode format constrains the design of the execution engine. For example, look at JVMs: JVMs are much more similar to each other than ECMAScript engines. Personally, I believe the "performance race" of the late 2000s / early 2010s would not have been possible without the wide range of experimentation that the lack of a standardized bytecode format afforded.
Not only is it hard to get all ECMAScript engine vendors to agree on a common standardized bytecode format, but consider this: it doesn't make sense to add a bytecode format for only ECMAScript to the browser. If you do a common bytecode format, it would be nice if it supported ActionScript, VBScript, Python, Ruby, Perl, Lua, PHP, etc. as well. But now you have the same problem as in #1, except exponentially increased: not only do all ECMAScript engine vendors need to agree on a common bytecode format, you also have to get the PHP, Perl, Ruby, Python, Lua, etc. communities to agree as well!
Well-known widely-used libraries are hosted at canonical URIs, where they can be referenced from multiple sites. Therefore, they only need to be downloaded once and can be cached client-side.
Many libraries use CDNs, so they are actually served from a location close to the user.
Wasm / asm.js
WebAssembly (Wasm) is a compact binary instruction format that is currently being standardized by the W3C and already being shipped in Firefox, Chrome, Safari, and Edge. It is, however, not designed as bytecode format for ECMAScript, rather it is designed as a low-level portable machine code and compilation target for languages like C, C++, and Rust.
Before Wasm, there was already asm.js, which had similar goals, but it was designed as a syntactic and semantic subset of ECMAScript, so you could run it unmodified in a non asm.js-aware engine, and it would work, just much slower.
It was never intended for large amounts of code (since client-side Java was supposed to be used for complex stuff) so the size overhead of comments and source text was just not a concern at the time.
We aimed to provide a “glue language” for the Web designers and part time programmers who were building Web content from components such as images, plugins, and Java applets. We saw Java as the “component language” used by higher-priced programmers, where the glue programmers—the Web page designers—would assemble components and automate their interactions using [a scripting language].
And another quote:
The answer was that two languages were required to serve the two mostly-disjoint audiences in the programming ziggurat who most deserved dedicated programming languages: the component authors, who wrote in C++ or (we hoped) Java; and the "scripters", amateur or pro, who would write code directly embedded in HTML.
Today the distinction between scripting languages and compiled languages is a lot more blurred, but at the time, catering to non-developers meant scripting language which meant no separate compilation step.
Ease of development
Embedding in HTML
<script> document.write("<input type=\"button\" onclick=\"alert('hello world')\">" </script>
Data use is probably not actually a problem.
To respond to the assumption in the body (since the wonderful response by Eric Lippert seems to have the actual questions quite well covered):
As for the rest of your questions, in many things, it is less useful to ask "what problems will this cause?" than to first ask "what benefits will this create?".