The Future of JavaScript
blog.chromium.org
blog.chromium.org
What makes JS powerful is its simplicity. "var" or no "var" gets you local or global scope. Done. Want a constant? Make a global variable and don't change it. Want to really make sure its unchangeable? Make an accessor function.
Scope in JS is very simple to understand and quite versatile once understood. Adding more layers of complexity does not help.
One of the strengths of js is that every thing is an object(almost). So the bit about "no more need to abuse objects as dictionaries" is a bit odd. Since most things are objects, you get the power to treat functions, types, literals, etc in interesting ways.
One of the strengths of object literals is that you can use them as dictionaries. obj.foo === obj['foo'].
I love getters/setters and some other improvements like strict mode. However I am very much against what seems like an unnecessary cluttering of the language without fixing some of the long standing problems like ==/===. For me, I almost never use == so it should be gotten rid of. Make == === === for example.
I believe Harmony sets allow you to use any object as a key, whereas using a plain JavaScript Object instance will coerce the key to a string and use that as a key. There is no non-hacky way to do this in JavaScript currently. You either need to us an array (making lookups O(n)), or give objects a UID to use as the key.
Even if you're just talking about sets with strings as keys, there are lots of edge cases waiting to bite non-experts. For example, what if you want your set to have a key called "toString"? Every JavaScript object already has that method, so if you naively test "set[key] !== undefined" or "key in set" with "toString" as the key you always get true. Ok, so use "set.hasOwnProperty(key)". Now what if you happen to have a key called "hasOwnProperty" and you mask the builtin function? So really you should be using "Object.prototype.hasOwnProperty.call(set, key)". Additionally, now you're making a function call, which could add significant overhead in tight loops.
>One of the strengths of object literals is that you can use them as dictionaries. obj.foo === obj['foo'].
That's not a strength when it means you can't use anything other than strings as keys. It destroys your ability to 'treat functions, types, literals, etc in interesting ways' if those interesting ways involve using them as dictionary keys.
>For me, I almost never use == so it should be gotten rid of.
Backwards compatibility.
I'm not really opposed to the changes to JS that are mentioned on the Chromium blog, but mostly because I don't have to use them. I don't really see the point of any of them -- and practically speaking, they won't see widespread use anyway until IE 9 dies out -- but maybe I'm just not imaginative enough to understand yet.
It is true that JavaScript's scoping rules are pretty simple, but less is not always more. Once you have learned all the patterns and gotchas, JavaScript's scoping is quite usable. But the fact that you have to use patterns where you wouldn't in another language means JavaScript is making work for you. If JavaScript had Lispy macros or some other way of altering the language, I might be more inclined to agree that the fundamental simplicity is a good thing, but without that power, the simplicity means that when you're doing complex things, the complexity missing from the language has to be written out explicitly in your code. Right now a lot of JavaScript code is a mix of function pasta and things that should be function pasta but the coder couldn't be bothered.
> I'm probably alone in saying this, but I hope most of this stuff never sees the light of day.
In any case, this stuff is seeing the light of day. It's been in Firefox for a while, and is now being implemented in Chrome. That means two independent implementations, which is crucial for the standards process, we can expect the other browsers to follow Firefox and Chrome on this.
So the bit about "no more need to abuse objects as dictionaries" is a bit odd. Since most things are objects, you get the power to treat functions, types, literals, etc in interesting ways.
I think this was about performance of the underlying data structure. In this sense, a specialized collections data structure will be an "object" too.Wrong. What makes it powerful is its ubiquity and the fact that it's de facto the only game in town for browser behavior scripting.
Want a constant? Make a global variable and don't change it.
That involved mental overhead (and team co-ordination) that makes it far less simple than "const foo".
Scope in JS is very simple to understand
Simple != intuitive or less prone to bugs. Brainfuck is also simple to understand, just a handful of rules. I wouldn't want to program in it, though...
One of the strengths of js is that every thing is an object(almost). So the bit about "no more need to abuse objects as dictionaries" is a bit odd. Since most things are objects, you get the power to treat functions, types, literals, etc in interesting ways.
Yeah, the only thing you DON'T get is an actual dictionary. Without hidden values coming from prototype, without reserved words, etc.
This proposals retains all that power PLUS gives you an actual bloody dictionary, one of the three more basic data structures in all of programming, without the toil and the million edge cases objects as dicts have.
For me, I almost never use == so it should be gotten rid of.
So what you consider as strengths are the shortcomings, and what you consider as a problem is an actual case that it can be considered a neat feature. The way js is designed, "==" makes more sense, because it lets you use more dynamic equality checking.
Var is broken in js because it does not obey sensible semantics
for(var i....
does what you think it does. In every language except javascript it would declare i only inside the loop. In js it declares i in whatever function scope the loop is inside. A = function(){
console.log(this);
}
A.prototype.q = function(b){
this.h = b;
}
imp = new A();
setTimeout(100, imp.q)
//later, in a response to a user event
console.log(imp.h)
prints undefined. Why? because in Javascript the value of this changes. There is literally no other language where this is dynamically (as opposed to lexically) scoped.Don't even get me started on the lack of ints, the lack of static types (which means your IDEs suck).
As for why node decieded to go with js? No idea, though it is one of the things I hate most about node. Haskell would have been a better choice, as would Lua.
Scoping is broken in pretty much all modern dynamic languages.
Take Ruby for example:
i = 10
[1, 2, 3].map{|i| i * 3}
[1, 2, 3].map{|j| j * 3}
puts i # prints 3
puts j # raises undefined local variable
Or let's look at Python: def maybe_append(b=[], n = None):
if not n is None: b.append(n)
return b
print maybe_append(n=20) # prints [20]
print maybe_append(n=30) # prints [20, 30]
So there's only a single instance of [], so that the default argument "grows" over time.setTimeout( 100, imp.q) // DOESN'T DO ANYTHING, 100 is not a function
And if you thought about it for more than a few seconds, you would realize that the behavior of "this" makes sense and is less magical than in other languages. Since "imp.q" is not a function call, it is a reference to a function, you are simply passing in a function. Since javascript "classes" are just objects with function references, you can't somehow decide what object "this" should point at unless it is explicit. In the case of "foo.bar()", it is explicitly "foo". In the case of setTimeout( foo.bar, 100), all setTimeout gets is a function pointer with no notion of what this should be.
Anyway, the rest of your gripes just sound like you don't like untyped languages, which is personal preference and I guess I don't share your preference.
And it may be nice to have 'just objects with function references' when you are writting a few lines of code to validate emails or something like than. When you start pushing 2k lines of code and more than two developers you need much more structure than than that. Anyhow C++, Scala and C# all have reasonable understanding of this. If all you have are function points, don't have this.
If you are trying to be sarcastic, you are not doing it right.
And I've only been using it for a few years. It really isn't hard to get past the bad parts.
Sure you can. Node.js is not even that impressive: similar solutions exist in lots of other languages, Ruby, Python, even Java. The Node guy wrote Node in JS for two reasons: V8 was a good and fast environment, and JS didn't have much existing libraries, so people could start making new async libs.
> No surprises, no more need to abuse objects as dictionaries.
... pretty much every single file of JS ever written is "guilty" of this.If that was an abuse, what are JavaScript objects supposed to be? What's the difference between them and a dictionary?
var dict = {};
dict["hasOwnProperty"] = true;
dict["toString"] = "Bob";
dict["valueOf"] = 7;
... then you're in for a rough time.Maps/Set/WeakMap properly support arbitrary objects as keys.
For one simple to understand != intuitive or easy to use. Chess rules are easy to understand too, you can learn all of them in an hour, but try to play the game with any quality. So this being simple to understand does not mean its simple to code around and debug.
You are also misguided in that this addition "complicates the language". It's not like adding generics or monads or some crazy new feature. It's one of the most fundamental datatypes, implemented properly for the first time.
So, instead of saying Object foo; foo.x = "a"; when you need a dict, and having to guard against all consequences, you get to use Map foo; foo.x = "a"; and there is nothing you have to think about anymore.
dict["valueOf"]
since that's obviously a bad idea, but you might write: dict[user_provided_string]
Since user_provided_string could be anything, you are susceptible to unexpected behavior if the string ends up being "valueOf".It would be great if the post included good examples of how these things are going to produce better code. Maybe my imagination or experience is just insufficient to see the great win here.
There are workarounds of course, but the ironic result is that when you want to use a js object as an actual hash/dictionary (something we are repeatedly told js is great at), your code devolves into a defensive programming mess, requiring constructions like Object.prototype.hasOwnProperty.apply, etc.
dict[ "_" + user_provided_string ]
then nothing can ever go wrong. This does have an annoyance around the leading underscores. But you can get around that by adding simple accessor methods. And now you have a real dictionary.But native dictionaries have the ability to store complex objects as keys. This is kind of nice. However they are likely to create traps like making these 2 different:
my_dict[5], my_dict['5']What about the key "_proto__" (forming "__proto__") [1]? That will screw up your dictionary badly.
It's not so easy. Which is why you want Harmony Maps and Sets in the first place.
[1]: https://developer.mozilla.org/en/JavaScript/Reference/Global...
Yes, let's add some arbitrary personal convention you have to follow and keep track of, because it's so much easy than a proper Map type...
But you can get around that by adding simple accessor methods. And now you have a real dictionary.
You still don't have a real dictionary, keys are only strings for example. You just have another lame-ass implementation of a dict in JS that avoids some edge cases and still falls prey to others.
However they are likely to create traps like making these 2 different: my_dict[5], my_dict['5']
Those two ARE different.
..will not have any of those properties. Works only in v8 though, AFAIK.
But I don't think it quite solves the problem jashkenas is talking about, which is that JavaScript automatically calls certain properties of your object behind the scenes in many situations, so you aren't completely free to use any key you want. You might be able to use a no-proto object like this as the internal datastore for a map, but by itself an object with arbitrarily assigned keys would cause a lot of problems unless you were very careful with it.
In ES.next, you've got to use pythonesque `import y from Bar` statements which introduce a frustrating correspondence between the lexicals of your own program and the export names of the module you're trying to use which is really terrible if you're using as many modules as I typically do. I presume ES.next will invent even more specific syntax for some sort of `as` like keyword to side-step this but it seems so unnecessary when javascript already has a module system that is so very good as is exploding in popularity and use (>7000 modules now on http://search.npmjs.org/).
I understand what they're trying to do with static analysis too but you can already pretty much do that and I've done it. It's not hard at all, just ignore require() statements that don't contain strings when you walk the AST, like this: https://github.com/substack/node-detective
Inventing more syntax instead of just adopting a far better module system that has seen actual use in practical situations is such a shame and ES.next is rife with this kind of prescriptivism. It irritates me to no end and I sometimes want ES.next to die a quiet death in obscurity because of it.
Additionally, your static analysis must be unsound if modules are mutable. The problem is not figuring out which module is being imported, it's figuring out whether the bindings are mutated. CommonJS cannot guarantee that modules haven't been mutated.
And yes, you can rename imports. Why is "var { foo: bar } = require('baz')" so much better than "import { foo: bar } from baz;"? They look the same to me, except that the ES6 one is better for static analysis and better for optimization.
1. What if I want to have my module be a function, the way that $ in jQuery is both a function and a namespace for the rest of the functionality?
This is an important use case, and so we're going to support it in ES6 modules. You'll be able to say:
module $ at "http://jquery.com/jquery.min.js";
$(...)
$.ajax(...)
2. Modules will requiring using names without the prefix.This isn't true. As you can see in the above example, you can just use $ directly, even though it's a module. You can also say:
import ajax from $;
but you don't have to.3. We should just standardize the node module system.
Of course, there are many different module systems being used in JS code right now, and we could just pick one. Unfortunately, we couldn't just pick the node module system, since it's fundamentally synchronous, which is great on the server side but not in the browser.
However, we feel that we can bring real advantages to JS programmers by extending the language -- we can simplify using modules, we can make some patterns direct that currently have to be written using callbacks, we can better support encapsulation, and we can allow engines to use knowledge about modules for optimization.
3. Static analysis is doable even without a static module system.
The static analysis you describe, while useful, isn't going to tell an engine statically where it can go to lookup references to an export from a module. That means it's not useful for many of the optimizations that we want to enable. Additionally, your analysis isn't sound -- it can miss uses of `require` that are generated dynamically, for example. Again, that means that engines can't use it for optimization.
Finally, if you have comments about ES development, I strongly encourage you to make them on es-discuss, rather than on HN, where they are more likely to have an impact on the development of the language.
I'm strong user of Node.js style modules (also for client side) and I see modules as proposed for Harmony as big step forward. It's basically same concept but with dedicated syntax (well put syntax) and some extra powerful features.
I've once setup some comparison how today modules written for Node.js (like modules that are functions) will look in harmony. I didn't spot any issues. See slides 86-87 at http://www.slideshare.net/medikoo/javascript-modules-done-ri...
export function ajax(...) { ... };
// NB: not how jQuery actually works ;)
export this(query) { return document.find(query); };
The `export this` syntax specifies how the module instance object behaves when called as a function.In particular, I still really dislike the special import "local renaming" syntax since it seems so unnecessary compared to just having an import keyword that returns an object and letting the programmer extract and manipulate the relevant entries. You would still get the static analysis benefits of having static imports without all the awkward complexity of the current proposal. Wouldn't it just be the same thing to Object.freeze() the export object but without any syntax magic necessary?
Plus, overloading `module` to do loading AND defining with the `module Bar at "uri"` form seems really wrong. Why can't it JUST do defining and just let import do all the loading?
module m at "blah.com";
... references to m go here ...
We also want to support other use cases, like binding the exports to names available in your local scope. We understand that not everyone likes that style, but I don't think the language should mandate a particular side in that fight.Second, the `module m at "URL"` form above is a definition of `m`. It's just that you're allowed to refer to that binding as an object too. Would you rather have:
module m at "example.com";
import m;
That just seems like unnecessary typing to me.1. `module` is about defining the modules that you're using in your program, some of which might be remote resources. `import` is about bringing names into scope.
2. Using `module` this way lets you, the client, decide on names for modules, which means we don't have to rely on module authors to come up with and use naming conventions (net.substack.airport, anyone?).
This feeds back into 1, where we use bindings to reference modules, even remote ones.
Also, if a module is never referenced or imported, then the resources shouldn't need to be pulled down at all. So partly it depends on the way you look at things.
Adopting a hard-coded, python-esque module system is a step backwards from what we have now, and I doubt will be used much, if at all -- why give up features that already exist (and will continue to work)?
sigh
ANSI C was quite a big change from K&R, even.
> (Caveat: Iteration over collections is not yet specified)
Is this a joke? Is not iterating over dictionaries and arrays javascript's most glaring weakness?How hard would it be to agree on something like foreach, that, you know, actually works the way you think it should work.
for i,v in blah
, where i is an index of an array, or a key (not a property!) of a dictionary really too much to ask in 2012?No wonder people are using coffeescript.
for item, index in list
... here's the same over an object ... for key, value of object
... here's the map() ... mapped = for item in list
... here's the filter() ... filtered = for item in list when item.isActive
... and so on.Also, for `Map`, how do you think `foreach` should work? Should the function be called with (a) the key, (b) the value, (c) an array with both the key and the value, (d) the key and the value as separate arguments?
Similarly, in the statement:
for(let x of some_map) { ... }
should `x` range over the keys, values, or key-value pairs of the map?[1]: https://developer.mozilla.org/en/JavaScript/Reference/Global...
Somehow going from one keyword for variable declaration to three doesn't seem like a good way to reduce errors.
However, they cannot change var's semantics and retain backwards compatibility. So they did the next best thing: introduce a new keyword for variables that work like they should. That keyword is 'let'.
As far as I am concerned, if your JS is known to execute in an environment with 'let', 'var' might as well not exist. Too many subtle errors — “what do you mean, braces don't actually delimit scope?”
"what do you mean, braces don't actually delimit scope?" I would answer, they don't. Because this is not <insert your favourite braces-delimit-scope language>. This is javascript. Nobody should have ever told you that braces delimit scope here.
It's not like this is some extremely rare issue that you aren't going to find documentation for. A quick run through a basic tutorial would suffice.
So how is it that 'let' works the way variables should? It works differently, that's all. So now you have to know how let is different from var, why it was introduced, when to use let, when to use var, etc.
Then when you see code using let, you're going to have to determine whether the person used it because they know what they are doing, or because someone just told them "let is the new var" and they use it all the time. Same for var. There is an added level of uncertainty when reading code. And and added level of complexity in explaining the language. You haven't gotten away from explaining how var works. Because you're still going to see tons of code using it.
Wouldn't it be easier to just learn how to use var properly?
In my opinion, unnecessary surprise as a result of historical implementation artifacts is a design bug.
Yep, need to learn about it. Yep, you can, and write good JavaScript. But that doesn't mean the design is good.
Yes, it's not this that makes it a bug, it all the other horrendous stuff it entails...
Wouldn't it be easier to just learn how to use var properly?
No, it would be easier to fix it. Call it argument by authority, but I trust Brendan Eich more than some random HN dude on this.
JavaScript I not c. JavaScript is not Java. JavaScript is not python. If it was one of those languages it would be named that and there would be no JavaScript books, just dom and browser books.
But it is not those languages. It is different. If people don't learn the language they cannot be expected to write code.
People always bring up var for some reason. Why var? Why not closures? Javascript is often the first place programmers come across this concept and it always causes confusion and bugs at first. Should we get rid of them? Of course not!
Can you conceive the existence of a language that has got some parts wrong, and maybe awfully wrong?
Well, my point, and that of a lot of others, including Cockford and Eich, is that Javascript is such a language. And that the fucked-up scope it has, is one of such parts.
JavaScript I not c. JavaScript is not Java. JavaScript is not python. If it was one of those languages it would be named that and there would be no JavaScript books, just dom and browser books. But it is not those languages. It is different. If people don't learn the language they cannot be expected to write code.
People don't want Javascript to be Python, C, or Java. The want it to be a better Javascript and shed some of the BS that was added at its' conception due to a tight deadline. Being "different" than other languages is OK. Being brain damaged is not.
People must learn the language, but that doesn't mean they have to put up with mistakes in its' design and not correct them. Just like people corrected bad stuff in K&R C with ANSI C, Python with Python 3, Ruby with 1.9, etc.
You seem to have the wrong impression that people complaining are lazy programmers who don't want to bother to learn the language. They are not. Brendan Eich is a JS guru and implementer. Douglas Cockford invented the damn language! And he admitted that there are bad parts in it, that need to be corrected. He even made a book on how to work around those, called "Javascript, the good parts".
People always bring up var for some reason. Why var? Why not closures? Javascript is often the first place programmers come across this concept and it always causes confusion and bugs at first. Should we get rid of them? Of course not!
Because discussing what must be corrected is not about whether it's confusing to newcomers of not, it's about whether something is nicely designed OR a kludgy disaster.
Closures, while confusing to some, are a standard programming feature and a nice addition to Javascript. In contrast, var (and JS's scope rules) are just things that the JS creator gotten wrong. They need to be corrected.
That's a great idea. I'd be all for shedding some of the bs for a better javascript. But the var issue isn't being solved by shedding anything. Var will still be there. But now the language will be even more complicated because of some var workarounds being added in. In the end, you still need to deal with var.
For legacy code, yes.
But now, if you target modern browsers, you can build a whole web app using only let, and not be bothered about var and the old scope system at all.
Er, not quite sir. Brendan Eich invented the language. He is indeed a guru and an implementer; in fact the original implementer.
Crockford is a guru, and more importantly a pundit.
And the scoping rules are quite tricky in practice, to real programmers. To take an example from today:
As for variable scoping rules, if you have never been bitten by loop counters being used improperly, then I assert that you haven't programmed enough javascript. There are so many ways to mess this up: forget a var declaration and it leaks scope -- and since you can only declare it once, you can't just always use "for (var i = ...". If you want to defer access to a loop counter, then you have to remember to explicitly pass it by value to an inner closure. That last one in particular bites me all the time -- JSLint at least warns you about the first one.
What's worse.... now you still need to learn how var works anyway! It isn't gone. I see 0 reduction in confusion and +3 increase in cognitive load.
Would be really unfortunate to have to wait for a native OrderedMap/SortedMap/TreeMap until ES7.
But please, search the archives first and be aware of what discussions have already taken place on this subject. =]
Ouch. I guess the end of 2013 is sooner than never.
"Committee" and "designing" in the same sentence gives me the shivers.
But the features they're describing actually sound pretty reasonable. Actually a lot of them seem to already exist in Lua ("proxies" sound like what are provided by Lua's metatables, and Lua already has weak tables and lexical scoping).
You seriously think Dart is anywhere close to taking over Javascript?
It has the old Proxy.create() instead of Proxy.for(), which is the way to define a proxy as per direct proxy API.
So if anyone wants to implement proxies in current Chrome, they have to refer to the old proxy API (which is why they have linked to it).
You can also use new JavaScript features the same way you use other new browser features like WebSocket or cross-domain xmlHttpRequest: when you've determined that all the modern browsers implement the feature you care about. The browser vendors reach a point where they stop supporting older versions with security updates and similar; I'd consider it reasonable to stop supporting a browser when it stops receiving security updates. (You should also evaluate your audience and the browsers they actually use.) At that point, older browsers get the same experience as links or wget: they get the content, but not the enhancements provided by JavaScript or whatever other features you use.
This is the Web, after all. An interconnected world-wide community. Why isn't there some sort of voting process that anyone can give their input for ECMAScript's drafts? Right now it's an Oligarchy. At the very least, an electoral college-like system would be nice.
I think they should be very conservative about what they propose. It should be a very obvious improvement to the language or have a lot of design time spent on it. If all major browser developers are not on board (Microsoft?) then the improvement is much less valuable.
Of course it would incur a performance penalty, and for some stuff it might also be infeasible, but there could be some low level package just for this purpose, that provided options to add new keywords, bytecodes, etc.
The end result would be something like (in mock-pyjs glory):
if __language__.has_not("let") import __Future__.Let".
This would enable faster iterations of the main language, while keeping backwards compatibility. Anyone knows if anything like this exists?
(Well, you can do sort of this kind of thing in Lisp. But has it ever been attempted in a C-like syntax?)
P.S My understanding is that Python does not do this, it just makes the __future__ imported modules optional in the previous version of the compiler, but they are still implemented in plain C along with the rest of CPython, no?