The disadvantages of bytecode
Bytecode is a very specialized mechanism:
- There is no single bytecode to “rule all languages”: A good bytecode is intimately tied to the language that is most frequently compiled to it. It is thus impossible to define a bytecode that works well with all languages, especially if you want to support both dynamic and static languages.
- Bytecode is inflexible: it ties you to the current version of the language and to implementation details such as how data is encoded. Especially with regard to language versions, you need to be flexible on the web where you have many combinations of
language version(s) sent by the server × language versions supported by the browser
Quoting Brendan Eich :
Now, of course, you could say “Let’s version the bytecode”, and then you’re in version hell. The web really doesn’t like to have that kind of versioning. There’s a saying in the WhatWG that versioning is an Anti-Pattern and I agree we should avoid brittle a priori versioning, or heavy-handed versioning. If you look at Flash, it’s gotten into a situation where it has to support versions going back to Flash 4. They have to ship ActionScript 2 as a separate interpreter along with Tamarin. This is the hard row you hoe when you do make detailed choices in a lower-level bytecode, I think, and when you simply have an installed base that can’t be upgraded or doesn’t use a common source language.
Source code is not that bad – it’s meta-bytecode
At first glance, it seems like a suboptimal solution to use source code to deliver programs. At second glance, it has the benefit of flexibility and, with a little work, it can obtain much of the efficiency of bytecode.
- Source code abstracts over different language versions: Keeping the delivery format of a new language version backward compatible is easier with source code than it is with bytecode.
- Source can be quite compact: There are two ways of making source code more compact. First, minification – a transformation of source code that maintains the semantics while decreasing the size. For example, minification removes comments and changes variable names to be shorter. Second, compression. After minification, one can apply a compression algorithm such as gzip to achieve further reductions in size.
The remaining bytecode advantage
The main remaining bytecode advantage is that (static and dynamic) analyses can be performed ahead of time and delivered alongside the bytecode. The closest to bytecode one can get without losing the advantages of source code is to use the abstract syntax tree (AST) produced by a parser. The research project JSZap 
does just that:
- Faster parsing and well-formedness checking (including security checks).
- Reduced program size (by approximately 10% compared to minification plus gzip compression).
Dart’s snapshots are an extreme kind of ahead-of-time analysis that can probably not be duplicated by a cross-VM format. They improve application startup time. Quoting “The Essence of Google Dart: Building Applications, Snapshots, Isolates
” by Werner Schuster for InfoQ:
... the heap snapshot feature ... is similar to Smalltalk’s image system. An application’s heap is walked and all objects are written to a file. At the moment, the Dart distribution ships with a tool that fires up a Dart VM, loads an application’s code, and just before calling main, it takes a snapshot of the heap. The Dart VM can use such a snapshot file to quickly load an application.
- “Bytecode Standard In Browsers” – A Minute With Brendan Eich
- A first look at what might be in ECMAScript 7 and 8