>> DAVE HERMAN: OK, I'm Dave. A lot of my colleagues call me DHerman; that's my GitHub
ID, it's my IRC nick. I'm @littlecalculist on Twitter, which is a long story; you don't
want to know where that comes from. That's where you can find me.
I love JavaScript. I think it's a fabulous language; it's got some really brilliant ideas
in it. It's also got a few bad parts that people have been known to talk about. I love
JavaScript for the language that it is and for the language that it really wants to be.
That's a big part of why I serve on ECMA, the organization that Doug mentioned, where
I represent Mozilla. You just heard this, you know this.
All right. Life is short -- let's get to the dessert first. What you want to know is what's
going to be awesome about the next version of JavaScript. This is my whole talk in one
slide. This is what I'm going to talk about. There's so much awesomeness coming in ECMAScript
Edition 6, which will be the next version of the standard, that I figured I'd just start
with the stuff that I thought was the absolute most awesome parts, the parts that I am the
most excited about. That's Batman.
There's going to be a lot of code on these slides, so I hope everybody's had their coffee.
You may start to go cross-eyed at some point, and that's OK. We're going to talk about modules,
block scoping, generators, proxies, binary data finally, destructuring, rest-args and
defaults.
You've got to give Robin his credit; he's still pretty awesome. He's not Batman, but
this stuff is going to be awesome too. I hope you won't get bored at some point in the talk,
but if you do, that's OK, just don't tell me.
All right. I will sum up when I get to the end, so for those who kind of get tired of
the code samples, I'll try to give you the big picture too.
All right, so let's just go straight into it and start with modules. What are modules
about? Modules are about libraries. JavaScript has had a few massively successful libraries
earlier on in its history. Obviously YUI is one of those. But there were a small number
of the really big ones that really took off, and in the last couple of years -- particularly
since the invention of CommonJS, which is a sort of light weight standard for module
pattern -- people have started to figure out easier ways, standard ways, of sharing their
code. We're starting to see more and more libraries on the web.
I still think that there's a lot of friction caused by the fact that there's no one standard
way in the language itself to create some code that you want to share with other people
that they can just mix and match and compose with the rest of their code. To be truly great,
to fulfill its potential, I think JavaScript needs a module system built in. This is the
single feature that I'm probably most excited about in ECMAScript Edition 6.
Here's one way that I think about modules in JavaScript. When you start with a brand
new web page, when you start with that blank screen or that skeleton HTML file, the browser
basically hands you a lump of clay. We have learned over the years, especially with the
help of evangelists like Doug who have taught us good patterns and good ways of using the
language, that that ball of clay is incredibly malleable -- there's a lot of stuff you can
do with it -- but every new page that you get is just an unshaped lump of clay and you
have to start from scratch. That arm is the browser, and that ball of clay is an empty
script tag. We go through this process every single time.
We have ways of shaping these balls of clay. We've learned from Doug the module pattern.
If you want to create a module and you want to share it with somebody, this is...
>> AUDIENCE MEMBER: That's the dog balls.
[laughter]
>> DAVE HERMAN: I didn't hear.
>> AUDIENCE MEMBER: That's the dog balls.
>> DAVE HERMAN: This is the dog balls, oh.
[laughter]
I didn't need to know that. All right then. How do you follow that?
This is probably going to be an easy crowd to sell that this is maybe not the ideal code.
I mean this is how you write a simple module in JavaScript. You have to go through all
of this work. You have to make sure to protect your scope so you don't accidentally create
global variables. You have to package up an object that contains all of the exports that
you want to ship out to the external code. And there are a million little tiny variations
on this pattern, so there's not really one standard way of doing things.
I'm a language nerd so whenever I hear about patterns, I think of this quote from Paul
Graham where he said -- there's a longer quote, but he said essentially -- "whenever I hear
about design patterns I wonder if this is not an instance of the human compiler at work".
What he means is whenever you're writing the same boilerplate over and over and over again,
you're kind of acting like a piece of software. Shouldn't we automate the processes that we're
doing the same way over and over and over again? That's exactly what a compiler does.
I don't want to be a human compiler. I want to let the compiler be a compiler.
I don't know if anybody has ever actually said this, but "I like to think of design
patterns as the sincerest form of feature request." When programmers are writing the
same boilerplate code over and over and over again, they're saying there's something missing
in this language that I wish it had, and this is my workaround. It would be nice if I didn't
have to work around it.
If we just had modules directly in the language where you could say module Collections, you
could just write the code the way you meant it, you could decide what you wanted to export
simply with an export keyword. This already looks a lot cleaner. It looks a lot more like
the intent of the code. The module system that we're working on also lets you easily
import the exports from another module, and it even lets you import from external files
similar to the script tag in the web. Now you can actually do this from within JavaScript
itself.
That's useful on the web. It's also useful in other environments like Node.js where Node.js
has to rely on CommonJS to get access to external files. Node.js doesn't have script tags where
it can say what files it wants to get, so having a way actually within JavaScript to
load external modules means that this works on the server side as well as it works on
the client side. There's an important detail here which Common.js couldn't do because they
didn't have the option of changing the programming language itself; they didn't have the option
of changing JavaScript, which is what our job is on this committee. That is the ability
to preload these external files.
Part of the model of doing I/O in JavaScript, as we all know and I'll talk about this more
later, is that you never do any kind of I/O in a blocking way. You never stop your script
partway in the middle and block waiting for some bits to come in over the wire without
using a callback where you actually explicitly suspend your code. Here we're actually writing
the code in what looks like a blocking style but the important thing is that this happens
actually before any of the code runs, so it's not going to pre-empt your code at various
points. That also means that you give the browser the ability to do things like pre-fetching.
The browser can look at the source code and it can start pre-fetching the other scripts
that you're loading.
OK. I could talk a lot more about modules but we have lots to talk about.
Here is another one that I think has been a long time coming, and that's block scoping.
Here's the slogan, here's the way to think about ES6: let is the new var. Our goal with
ES6 is for you to basically be able to kiss var goodbye. We're not actually going to throw
var away -- we don't want to break all the existing code on the web -- but whenever you
write a new program you should never have to use var again. Let will just sort of work
the way we all wish var did.
This is my favorite example of a classic newbie mistake in JavaScript. You have some loop
that binds what looks like a local variable and you create a bunch of closures each time
through the loop referring to that local variable. Oops! It turns out that that's not a local
variable at all, that was hoisted to the entire function body. Doug teaches people you should
actually manually hoist your vars so that it's absolutely clear in your code whether
they actually are in scope.
But with let you can actually write the code the way you wanted to. You can have a local
variable inside of the block and this does what you want. Let is like var except that
it's block scoped instead of function scoped. This is the way var should have been from
the get go. Now we're going to do it.
SpiderMonkey has actually had this feature for years. It just was part of the ECMAScript
Edition 4 process that got cancelled, so it's taken a few years to bring it back through
the standardization process.
OK. I mentioned blocking I/O before, and that's the next topic. I'm curious -- how many people
here have used Python at all? Of those people, how many people have used Python generators?
A small number, OK, so it's not particularly commonly used. That's cool. I'm going to talk
a little bit about how generators, I think, can be a viable alternative to callbacks.
If you've never seen generators before, this is roughly what they look like. They are actually
very similar to the feature from Python. A generator function is like a normal function
except it has this star symbol next to the word function that just indicates this is
a slightly special kind of function. The thing about a generator function is you can suspend
in the middle of what you were doing and re-enter the function at an arbitrary point later on
in the program. The way you do that is with the yield keyword.
This is like one of the simplest generators you could write. It says yield three times.
How do you use that? Well, you call the function and what you get back is a suspension of the
function. It actually hasn't even started running yet. It's called the generator object,
and that's what we're binding to g here. You can resume execution of that function any
time you want by calling the next method on this generator object, and that'll start running
the function again and it'll stop as soon as it hits the next yield. This creates a
way that you can write functions where you can say, at any point, I want to stop what
I was doing and I'll continue later. That's always totally manifested locally in the function
because of the yield keyword. No one else can ever yield to you, so you can't call some
other function that yields you. OK, so this one we call next three times and we get 1,
2, and 3.
What does that have to do with callbacks? First of all, in the last conference I went
to I said we've all seen code like this, and some smart alec tweeted "uh, no I haven't."
I actually looked at the YUI documentation page yesterday and there's one of these on
the front page. Is there anybody here who has never seen code shaped like this? OK.
I think I'm right here that this is a pretty common phenomenon.
I like to call this the pyramid of doom. This is what happens when you want to do a bunch
of things sequentially but only after finishing some blocking I/O operation from the previous
thing. You end up writing in what pointy headed language nerds call continuation-passing style;
each one of these operations takes a function that you call a continuation, that is, here's
what to do when I'm finished with this previous operation.
This code is pretty mucky. It can be hard to read, it can be hard to work with. It can
be hard to change as the flow of the sequential operations you're doing needs to change. Some
people like to say oh come on, if you have these big pyramids you just need to learn
how to hoist your functions out. OK, well you can hoist your functions out one at a
time, give each one a separate name. It's not totally clear to me that this code is
that much more readable than the previous code, not to mention the fact that you end
up having to copy whatever local variables you needed to refer to in any of those nested
functions. They now need to get copied into the parameter lists, so you can get this explosion
of additional arguments. Besides, this is supposed to represent a sequential control
flow. Thank God we don't do this in the rest of our lives -- imagine if we had to write
our recipes this way. "Step one, page one: go to page two. Step two."
How can generators help with this? Well, generators give you this ability to say I need to stop
what I was doing and wait for something to happen later. That's exactly what you're doing
with callback-based I/O, with non-blocking I/O; you're saying there's some operation
that needs to finish before I can proceed with what I was going to do.
I've actually played around with a library that actually provides some conveniences on
top of this idea of using generators as a way of doing non-blocking I/O. It's called
task.js and you can find it on GitHub. It's not in production-ready state, but it's just
an experiment. It's open source so I welcome anybody who wants to play around with this
to join me. The idea is I have this spawn function that'll take a generator function
and basically put it into a scheduler. Every time you yield from what I call your task
function, you just send it a promise.
A promise is something that's starting to catch on in some other JavaScript libraries
like JQuery and Dojo. I couldn't find it on the YUI page; I think YUI doesn't have promises
at this stage. I'm not sure if there's a plan for that. But the idea of a promise is it's
just sort of a representation of... It's a value that represents I'm waiting on some
data to come in later on. If you have a promise API for reading over an XML HTTP request or
a promise API for setTimeOut, that promise gets passed into the scheduler and the scheduler
decides to wake this task back up as soon as the promise is ready, as soon as the promise
is fulfilled. What ended up with the pyramid of doom or with these many, many different
callbacks with names that are hoisted to the top level actually goes back to looking like
normal straight line sequential code, and the yield keyword is what tells you at each
point this is where we're actually blocking, waiting on I/O.
I'm really excited about generators. I think they're a nice sweet spot that allow us to
still have an explicit way of saying I'm suspending what I'm doing here. Yield still makes that
clear. You could contrast that with Java where with multithreading, at any point, anywhere,
somebody could pre-empt you. The system scheduler could pre-empt you, and you never know when
your state could be changing out from under your back. Here it can only happen when you
decide to allow it, so this is sort of like cooperative multithreading.
I think this is going to be really powerful. I hope that people will experiment with more
scheduling libraries like task.js. The fact that you can write your own scheduler also
means that you can do your own fun things like... This is on my to do list, I haven't
done this yet, but I want to have a record and replay version of task.js where if you
go into recording mode you can actually create a history log of every I/O operation that
happened in your test run, and then that becomes a replay-able test that you can put into your
test suite to get a deterministic sequence of I/O operations in your program. That could
be useful for testing. I think there are lots of experiments people will do with generators.
Next topic is proxies. Proxies are a pretty powerful meta-programming API. They're actually
a super powerful meta-programming API. They've gone through several iterations of API design,
and there's a more recent version of the API we've been discussing now that I think is
actually a little more approachable than earlier versions. So I'm showing you something like
what we think they might look like now, but this is still ongoing work.
A proxy basically lets you take some existing object and create a wrapper object that can
monitor anything that happens to that object. You can intercept get operations, set operations,
delete operations; just about anything you can ever do with an object. You can actually
create a monitor function that will be triggered whenever anybody tries to do something with
the underlying object.
One example people have talked about a lot for what you might do with proxies is data
binding. If you want to create some data collection in some place and you want to be able to respond
to that, say in your UI, you want your UI to automatically update when the data changes,
you could wrap that data in a proxy and basically respond to these data change events that happen
on the object.
Another thing you can do with a proxy is you can actually self host a lot more of the JavaScript
language. The DOM is this crazy library that has objects that don't behave like normal
JavaScript objects. We've actually built a faithful re-implementation of the entire DOM
in pure JavaScript at Mozilla because proxies are so powerful, they're so flexible, that
you can create your own object semantics.
This one is a pretty low level API. It solves a few important needs. There are also some
security patterns that proxies can be really useful for. When you're sending out some data
to untrusted code you can wrap it with a proxy that makes sure that only the select operations
you want to allow to happen, happen. I think it's going to take people awhile to experiment
with this, but it's such a powerful feature I think we're going to see a lot of creativity
around proxies.
Getting out of abstract land and into more concrete land, binary data has just been a
sore spot in JavaScript for a long time. Node.js has really struggled with this; they had to
create their own hooks for binary data because there was nothing in the language. We're seeing
this in web standards as well. There are these typed arrays that don't have a standard yet
to live in. They were invented for WebGL.
Typed arrays are powerful. They give you the ability to interact with binary data but they
have some usability issues, so what we tried to do was take typed arrays and kind of make
them a little more fully featured. What we did was we created a library of first class
binary data types. You can actually create a data descriptor yourself, in your code.
Here is an example of a StructType where it's a JavaScript value that describes Structs
that have an X field and a Y field that are both uint32s. Here's another Struct type that's
a color Struct that has 3 unsigned 8 bit integers. These are data descriptors; they're not themselves
Structs, they're just Struct types described in JavaScript. You can also combine these
StructTypes. You can create a StructType Pixel that combines both the Point2D and the color
that I showed on the previous slide. You can also create ArrayType. Here's Triangle as
an ArrayType with 3 elements, each of which is a Pixel. These are ways that you can mix
and match these type descriptors.
Well, what you do with a type descriptor? It's actually a constructor that you can call
to construct a binary data object. It's a JavaScript object but it's actually encapsulating
compactly allocated binary data. This alone could be useful for doing things like crypto-algorithms
that need to do a whole bunch of hashing over binary data and need to have a really compact
representation to be efficient. But it also is something... I don't have slides for that,
but it's also something we're going to make compatible with the various I/O specs on the
web like HTML5 File API, WebGL. I'm hoping that Node will integrate this as well once
it gets built into V8.
The idea here is once you have a way of actually controlling the exact representation of binary
data, now you can interface with the outside world. You can do things like reading from
a file or writing to a file, reading from a network socket, writing to a network socket,
that need to be in a very specific binary format. This is just something that you couldn't
really do with JavaScript except maybe in very clunky ways if people built in special
hooks.
Destructuring is one that, in my experience, when they first look at it people often don't
understand what it is or can't really see the benefit, but almost universally... I don't
think I've ever heard anybody who's started using destructuring who didn't totally fall
in love with it. Try it, you're going to like it. Destructuring lets you eliminate a huge
amount of boilerplate code for taking some compound inner structure and pulling out its
pieces.
Here's an example. Thing.color is some object, or we're expecting it to be some object with
fields called r, g, and b. I can just bind variables r, g, and b on the left hand side
here by automatically destructuring the thing on the right hand side. In this one line,
I've basically said ask for thing.color.r, ask for thing.color.g and ask for thing.color.b
and assign each one of those to a local variable r, g, and b. So it's really concise but it's
also really readable. I mean basically you're creating a pattern on the left hand side that
sort of matches the pattern of the data that you expect on the right hand side.
You can do the same thing with arrays. You can say I expect circle.center to be a two
element array and I want to ask for circle.center(0) circle.center(1). But all of those individual
accessors are just allotted here and the code just looks like how you envision the pattern
of the actual data.
There are tricks you can do too, like you can do automatic swapping of two variables
by pattern matching without actually having to name the temporary variable. That's mostly
a stupid human trick.
But destructuring works in more places than variable bindings. You can also use it in
your function signatures. If you have a function that takes an options object, instead of having
to name it options and destructure the object one piece at a time, you can actually destructure
directly in the head of the function. This is just nice and concise; it says just what
you meant.
Those kind of conveniences continue with rest-args and defaults. This is another addressing a
JavaScript misdesign -- is that a little harsh? I hate the arguments object, I wish the arguments
object would die. The arguments object is really painful if you want a few fixed arguments
and then the rest. In ES6 you'll just be able to say that directly, you can say "i, j and
then ...rest" means however many more arguments there are, I want them to be packaged up in
an array. The other thing that we'll get right which arguments got wrong is that it will
be an actual instance of array, unlike the crazy arguments object, so you can call .slice
on it without having to say Array.prototype.slice.call of rest etc, etc, etc. Yeah, I mean that's
a no brainer.
It gets better though. You can also use the "..." syntax when you're calling a function.
Here we're calling array.push and maybe we want to give it a few fixed arguments and
then splice in the rest of the arguments. You can do that with "..." as well. You can
use it with new, also. This is interesting. This second line is not expressible; there's
no way to write that code in ES3. In ES5 -- this is just one of those weird language nerd things
-- the bind function, the bind method actually gives you the ability to implement that code
in ES5 in a way that you couldn't do in ES3. Apropos of nothing, just an interesting side
point.
Default arguments, or default values for arguments. If you have an optional argument, width and
height, and you want to be able to just say here's the default value if somebody didn't
provide the argument, again, this just reads like what you meant it to say. So, another
no brainer.
And finally, this fits together with destructuring so you can put defaults into destructuring
patterns as well. I can have an options object, my function takes an options object, and I
can have optional elements of the options object and provide the defaults right there.
This is just pretty and concise and says what you mean.
OK, I think we've moved on into Robin territory. You have my permission to hate everything
that I say but I actually think that this stuff is still pretty cool.
Name objects are a slight generalization of object keys. In JavaScript we all know an
object key is just a string. Even array indices are actually converted to strings. So the
only thing that you can key an object property with is a string. That means that basically
all the data inside of an object is always transparent. So if you ever want to do some
sort of information hiding, any sort of encapsulation of data -- say because you keep tripping over
it, or you're trying to compose a couple of different objects and they're re-using the
same name for different purposes and you don't want them to step on each other's toes, or
you're in a security setting where you need to care about having some data that you don't
leak to an untrusted third party... There are lots of different reasons why we use information
hiding, and objects don't really give you a good way of doing it, except with Doug's
closures as objects pattern.
So how do you do that? This is straight off of Doug's web page. You create a constructor
function and locally bind the private data inside the constructor function, and any of
the methods that you want to have access have to be defined inside of the constructor body.
We all know this pattern. But there's a serious downside to using this pattern, and that is
that this .service, this service method, there's a new closure being allocated for every single
instance and it's an own property, it's not a method that you can share on the prototype.
If you want to have private data in an object, if you want some private fields of an object,
you're actually forced to create a new copy of each method, an own method for anything
that accesses it. So there's a memory tax for doing that in JavaScript.
What we've done is we've created a very small generalization of object properties which
is that an object property can be more than just a string; it can also be one of these
opaque name objects. A name object really doesn't have any data inside of it; it's just
a token that has a unique identity. You can create as many new names as you want, and
then once you have one of these names you can use it as a property key.
Here we created a new key -- I'm just calling it key here -- and that key is the name of
the property where we're storing the secret data. Now I can actually share one single
service method because service is defined in scope of my definition of the key object.
Now this one single method can be shared with all instances of container, but I haven't
leaked the key to anybody. I can choose to give the key to whoever I want, but it's just
locally scoped in this code so it's not shared with anybody else. Private names give you
a way of using objects to have private data in a way that doesn't force you to use closures
if that doesn't give you the memory usage you want.
Let's see. Custom object iteration. This is another one of JavaScript's features that
are just as clunky and hard to work with as the for in loop, so part of our task was to
see what could we do about reforming the for in loop. A common newbie mistake in JavaScript
is to use a for in loop on an array expecting that you're going to get the elements of the
array by value and not by key. But of course if I have an array with 3, 4, and 5 in it,
I'm going to get 0, 1, and 2 and a lot of code breaks because it expected that.
Really, for in just syntactically looks like it's saying give me everything in this collection
when in fact what it's saying is give me every key in this object. We talked about reforming
the for in loop and ultimately decided that there was just too much web code that was
built around an understanding of for in, the way it works now, so we designed a different
kind of loop that we called the for of loop. Of is not actually a reserved word in the
whole language, it's just contextual, so if you use of here in this one context it'll
understand this is a new kind of loop. But you can still have variables called of.
For of, by default, with Array is going to give you the array values. For of, by default,
with other objects we haven't decided. Maybe it won't actually do anything, maybe it'll
just throw because there's no actual iteration to find by default for objects. Or maybe we'll
have it default to for in. If you have opinions about this I'm happy to talk about them. Offline
we've been debating this lately. The key here is that you can define whatever iteration
protocol you actually want. We'll give you a few built in ones. Keys will be an iterator
for any... We'll give you an iterator for any given object that gives you the object's
keys. Values will give you an iterator for the values. Items will give you an iterator
for the pair of the key and the value, and here we're using destructuring in a nice way
to pull out both the key and the value.
But how do you write your own? We'll actually have a name object called iterate that you
can put on any object you want, where you get to actually define the iteration protocol
for this particular object, and you can share that on prototypes as well. If you create
your new custom collection class and you want to say this is the way that my collections
iterate in a for of loop, you simply define the iterate key on that prototype and now
every instance of that collection can be used in a for of loop to iterate over the values
of that collection. This actually leaves it up to the programmer to decide how they want
a for of loop to work.
For those in the crowd who are Pythonistas -- there were a bunch of you -- some of this
stuff should look awfully familiar. We have ruthlessly poached ideas from Python wherever
it's useful. Python's not the only one; we've looked at CoffeeScript, at Ruby, we've looked
at more egghead languages like ML and Haskell. We steal good ideas wherever we can find them.
Comprehensions. Anybody who's familiar with CoffeeScript or Python or even Haskell, these
might look familiar. These are just a really beautiful and elegant way of doing array processing.
This first version here that has the square brackets is saying I'm going to create an
array whose elements are the multiplication of X times Y, where X is pulled out of some
iteration over object one and Y is pulled out of some iteration over object two. It's
sort of doing a map over these two different functions and automatically producing a new
array. This is a really elegant way of doing array processing.
We also have people in the community who don't like array comprehensions and who have said
thing like "I got so excited the first time I saw array comprehensions I used them everywhere
and my code became ugly," so I would say don't use them everywhere. In CoffeeScript apparently
you have to use them everywhere, but in JavaScript you won't. You can use comprehensions if you
think they're good; if you don't like them you can just Map, for each, normal, for loops.
You can do it however you like. But comprehensions are a really elegant way of doing a lot of
simple array processing code.
The second thing here is actually a really sweet syntax for doing a lazy comprehension.
What this does is it produces a generator object that has these next methods. You can
one by one pull out the elements of this sequence and it'll only continue doing the processing
each time you call the next method. It's just like the code above except that you kind of
pull out values on demand. If you were doing a really heavy computation that can be nice
as a way of getting as much as you want or only doing the computation when you want to.
OK, the next topic is string templates. So much of JavaScript code involves hacking on
strings. I mean we're embedded in HTML, we're generating HTML, and the only feature that
JavaScript gives you for doing this is string literals and the plus operator. That's all
you've got. You could create string formatting libraries, and people do, and that's fine.
But it really would be a lot nicer to just have a built in way in the language of doing
what's sometimes called string interpolation in languages like PHP or Perl or even Bash.
This also relates to a feature called quasi-quoting from Lisp, although the people who designed
this called these quasi-literals and that term was so frightening that people got angry.
I just think of this as templating. I mean, this is what you're doing all the time. Whether
you're doing server side or client side JavaScript, you're constantly creating HTML code that
you need to embed some computed values into. It's really nice to be able to create the
string literal but escape back out into JavaScript to compute whatever arbitrary values you want
to splice back in. For those who are paying close attention, you may notice that this
actually is a multiline string and there are none of those evil line continuation backslashes
that Doug may have talked about last night.
Using the backtick instead of the ordinary quotes, that's how you do one of these new
kinds of string literals. You just automatically get multiline strings. If you don't like the
indentation aspect, which I've also heard Doug talk about, maybe these aren't for you.
But it doesn't have the danger of the backtick with the secret white space afterwards.
There's also an additional piece here which is that often when you're generating templated
code you are introducing a potential risk of various injection attacks, like SQL injection,
script injection. In addition to having the string literal we also have a convenient way
of being able to add your own sanitizers. I believe we're going to be working on having
some built in sanitizers; I'm not sure if that's the case. You can easily create, say,
a safe HTML sanitizer that makes sure that there are no script tags. There's just a convenient
syntax for plugging that right in. Some very smart security researchers worked very hard
on this and came up with something that they felt was both pretty simple and pretty robust
for preventing string injection attacks.
Here's another thing that you just can't do in JavaScript. This one's really painful.
If you ever try to create a hash table where the keys are objects, JavaScript really does
not help you. You could do it with absurdly bad efficiency -- you could store the elements
of the table as an array and do a linear search every time somebody does a look up -- but
that doesn't give you the efficiency that you expect of a hash table, so we're just
going to build in hash tables to the language finally. The current going name is Map. Map
is just a new object with get and set methods and some other conveniences like iteration
so you can store an object in there as the key and a value associated with it. Kind of
a no brainer, pretty straightforward.
But we also have something that's a little more subtle, which is an API called WeakMaps.
WeakMaps let you store any object as a key with any value, but the reference is actually
held weakly. If the object becomes unreachable anywhere else in the program, other than in a WeakMap, the garbage collector
will decide that this is actually an unreachable value and it'll automatically make it disappear
from the map. That may not always be what you want; sometimes you want a map where it
really does hold onto this object strongly.
But say for example you have a program like Yahoo! Maps. That's Google Maps, sorry. Say
you have some sort of anonymous mapping program and you have a bunch of tiles on the screen.
We know how to do this: we lazily generate the tiles, we lazily download the tiles that
we need as the user pans and zooms through the map. But once you start having too many
tiles you might want to start expelling some of those tiles, because you don't want to
fill up all your memory.
Let's say at the same time you also have these markers for doing different searchers, and
the markers can come and go. Maybe you don't want to store those markers actually directly
in the objects, you want to think of those as being a separate data structure from the
objects. But you want an association between each tile and what marker's actually landed
in that tile. With a WeakMap you can create that association -- say this tile corresponds
to these markers -- but if the tile ends up disappearing because the user hasn't panned
over there for a while and you illuminate the tile and the garbage collector decides
it's time for the tile to go, the WeakMap will also make the tile disappear from the
map.
This is one example. There are lots of subtle cases in framework code where WeakMaps come
up. Again, this is one of those low level power features. There are also some higher
level patterns that you can do with WeakMaps. For example, you can treat a WeakMap as a
kind of private data field. If you want some data that nobody else knows you're associating
with this object, rather than putting it in the object you can store it in this side table.
Nobody has to know about the side table that you're hanging onto.
But again, I expect the WeakMap isn't one of those things that people are going to reach
for for every problem, but sometimes for low level code, framework code, or memory intensive
code, they can be indispensable. There's no way to do this in JavaScript. There are no
hooks to the garbage collector in JavaScript directly.
OK, that was a whirlwind tour of all of the features. But I want to reassure you that
this is not just a grab bag here. We didn't just say let's think of every cool idea we
can ever think of and try to shove it into the language. There is a bigger picture. First
of all, we actually have a sort of mission, we have a main page on our Wiki that sort
of describes the overall goals of this process, the goals of what we're trying to do for ES6.
There are more than just this, but I think this is the heart of it: we're trying to make
JavaScript a better language for complex applications because JavaScript programs in 2011 do not
look like they did in 1996.
We want it to be a better language for libraries, because we want to support people creating
bigger and better libraries like YUI. And we also want to support code generators. I
haven't talked about that too much in this talk. For example, I didn't talk about proper
tail calls, which I expect code generators might be able to make use of. Binary data
might be useful for code generators. But code generation on the web is a fact of life, and
it's not going to change. As much as I love JavaScript, I have no problem with people
deciding that their language is better for their needs and they would rather compile
it to JavaScript. That's, I think, a trend that's going to continue.
So we want to support all of those use cases. We're motivated by use cases. But one of the
worst things you can do in language design is say I've got all these use cases, I'm going
to find the first solution I can that solves it, and add them all together and that's my
language. What you get with that is Frankenstein's programming language. Instead what you have
to do is you have to say how can we solve these features with the simplest, most general
and most composable features that we can? When you do that you end up simplifying the
language, you end up with this as simple as possible but no simpler programming language.
When you take all of those pieces and you put them back together, you also have to make
sure that there's a cohesive design to the whole thing.
This is a process. I brought this up in one of the TC39 meetings maybe about a half a
year ago. There's a concept -- I think it started in theology and then ended up kind
of getting picked up in various branches of philosophy -- this idea of the hermeneutic
circle or the hermeneutic spiral. It was originally thought of as a way, like I said it was theological,
it was a way of reading the Bible, but it got generalized to a way of thinking about
learning, a way of thinking about epistemology.
Whenever you're trying to understand something you sort of study the individual pieces, and
as you understand the individual pieces together you understand the whole better. But as you
understand the whole better that gives you new insight into how to understand the individual
pieces, so you kind of go around this circle over and over again. I like the idea of the
spiral because there's this idea that each time you start back over through the process,
you're refining and getting closer and closer to your goal.
That's sort of how we work. We study the use cases, we study the individual pieces that
we're working with. Then we take a step back and we have to look at how does all this stuff
fit together. Then that ends up kind of feeding back into how we think about the individual
pieces.
I showed you a whole bunch of features, but these are actually the culmination of years
and years of work. Some of them are still ongoing work. But I think as a standards committee,
we have our debates but we actually work pretty well, and I think we all have a good eye for
when things are starting to feel like they've gone off the rails and how to cull back out
complexity.
The last piece is that we do our work in the open, and that means that every community
that I talk to is a part of this process. We're not doing this alone, we're doing this
with the community. We have an open Wiki that shows all of the work that we're doing on
the committee, and we have an open mailing list, es-discuss -- if you just search for
es-discuss you'll find the Mailman information -- where anybody can participate and lots
of lots of people do. Some people find es-discuss daunting; there's a lot of smart people, there's
a lot of people with very firmly held opinions, but I promise you that I will take seriously
any question that any of you has. We all try to treat everybody with respect and I think
we actually have a pretty great group of people.
The last question that I always get is OK fine, but I have a job to do; this all sounds
neat and pretty but when can I actually start to care about this? That's a totally fair
question, I mean the standards process is working on a different timeline than you are
in your day to day job. IE6 is going to be around for awhile; there's only so much I
can do about that. At Mozilla we do our best to kill IE6, but...
I think the best that we can do is the following answers. One, we have to work as fast as we
can on the spec. The sooner we get this out, the sooner the browser vendors can start reliably
shipping it and that will make that future where ES6 is a reality that much sooner. That's
still not this year, though, so that doesn't immediately answer the question.
But it's also important to recognize that we have a 2013 ship date for this spec. That's
our goal for the spec. That doesn't mean all of these features are hitting browsers in
2013; in fact a lot of the features I talked about today have been shipping in Firefox
for awhile, and V8 is doing serious work on this too. This is not just a Mozilla going
it alone thing. Google is working very hard on a number of these features. Proxies are
in both V8 and SpiderMonkey. WeakMaps are in both V8 and SpiderMonkey. V8 just shipped
Maps and the companion library sets, which I didn't talk about. We are getting ready
to land our first version of a patch for Maps and Sets. We have block scoping. V8's working
hard on block scoping. So a lot of these features are actually landing a lot sooner than the
spec. They may get tweaked as the spec stabilizes, but that means that these features are going
to start landing pretty soon.
The final piece is not something that I can hand you as a reality today, but it's a possible
approach that Google has experimented with and I would like to experiment with -- I haven't
had as much time as I hoped so far -- and that's the idea of compiling to ES3, compiling
to the de facto standard JavaScript that reaches all the way back to IE6. A lot of the features
that I've described here are actually pretty easy to de-sugar, to rewrite into existing
JavaScript. Things like destructuring or rest parameters, those are actually pretty easy
to compile. We could make CoffeeScript-like transfilers for the new version of the language
that you could use as a stop gap until enough browsers actually start shipping the standard
language so that you could actually be writing your code in the new language before the browsers
actually support it.
Again, this is not something that I have a working product here to hand you, but I do
have an open source project called Narcissus that we have started working on this on. Anybody
who's interested in working with languages, compilers, or the standards process, I encourage
you to participate because it's actually a lot of fun to work on.
So that's it, that's the future. I think it's looking pretty bright. Thank you very much.
[applause]