Crockford on JavaScript - Level 7: ECMAScript 5: The New Parts




Uploaded by yuilibrary on 14.11.2011

Transcript:
>> DOUGLAS CROCKFORD: Good evening. This is the seventh in the On JavaScript series. In
the previous episodes we've been talking about ECMAScript 3, which is the version of the
standard that's been in the field for the last decade. Tonight we're looking at ECMAScript
5, the new parts.
First to put this in context; we're talking about a standard and the effect of the standard
on the world. Any change to a standard is an act of violence. It's like putting a knife
into a person. Is it ever right to put a knife into a person? Yeah, sometimes it is. Surgeons
do that. A surgeon will make a determination that the benefits that could come from cutting
someone open and fiddling with the things inside of them is worth the pain and the expense
and the inconvenience and the risk of infection and complication.
Working with a standard is not quite so dramatic, but it's a similar set of tradeoffs. For example,
when you make a new standard it means now there are two standards, and that introduces
problems that didn't exist when there was one standard. That's particularly a problem
on the web because we're going to have old programs, or old sites, and new browsers,
and we'll expect those to work. We'll have new programs in old browsers and we'll expect
that to work. There are combinations that are really difficult to manage since we'd
expect to have compatibility going backward and forward without breaking anything. That
turns out to be really hard, and it's especially hard in the web. That's going to color a lot
of what I'm going to talk about tonight.
The reason this is a good time to talk about ECMAScript fifth edition is that we're beginning
to see complete implementations coming out and are now being seen in the world's best
web browsers, so this is a good time to look at it.
A little bit of history. The third edition, which is what we have been using, has been
in the field since 1999. In 2009, the ECMA General Assembly approved what we're calling
ES5, the fifth edition, which actually is two standards; it is the standard for the
default language and it's the standard for a new strict mode language. They're both described
in the new standards document.
Which of those languages should you use? I'm recommending that in the short term you be
working in the intersection of ES3 and ES5 Strict, and in the long term you should be
working with ES5 Strict. ES5 Strict is going to be the future of ECMAScript. Future editions
will be based on that. There might be temptations to be using ES5 default; my advice is to resist
those temptations.
Some people will be disappointed to learn that what we're trying to do with ES5 is to
make a better JavaScript. There were some people who were hoping that we would either
kill it or that we would transform it into a completely different language, turn it into
Java or something that's more conventional. We didn't do that; instead we improved the
language to make it better at what it is and improving its operations, but not substantially
changing it.
There are no big changes to the language; instead we made lots of little changes. We
made the standard conform better to reality. There were some places where none of the browsers
implemented what the standard said and all the browsers were right. In those cases we
corrected the standard to match reality. There are some cases where the browsers did not
disagree, and we had a three out of four rule: if three of the browsers did one thing and
Internet Explorer did something else then we would conform the standard to what the
majority was doing. Microsoft was very good at working with us to do that.
It's difficult to change standards in the web because of the likelihood of breaking
things. Any place where we found that the browsers were in general disagreement, we
took that as license to make changes. In some cases we discovered or found ways of putting
good behavior into the language in places where we were hoping, at least with respect
to the web, people were not using the language.
As a result of all of these things we think that interoperability will be improved. Now,
it turns out interoperability with JavaScript is already really good, but it will get even
better. The main place where it gets better is in the edge cases. I recommend that you
not be at the edge anyway, because that's the source of pain and misery. If you stay
to the middle of the language it's already very interoperable, but it gets even better.
If you program pretty well, this class of spec improvements and consolidation shouldn't
have any impact on you. Your code should continue to work fine.
The goals for ES5 were number one, the most important prime directive was don't break
the web. We didn't keep this, or we don't know that we didn't keep it but I expect that
we will break at least a little bit of the web. That's just the nature of the web, that
if you make any changes for the better, there are people who are dependent on it being worse,
and they will break. But we tried really, really hard to minimize the amount of breakage
we're going to cause.
We're improving the language for the users of the language and not for people who do
not use the language. We're not trying to make JavaScript more like Java now, we're
trying to make JavaScript better at being JavaScript, and I think we succeeded at that.
We gave a lot of attention to improving third party security so that we can do mashups,
and I'll talk a lot more about that later.
We did not set out as a goal to protect stupid people from themselves; that's just too hard.
Stupid people will do stupid things and we're not doing anything to prevent it. We've added
some powerful new capabilities to the language which will allow people to do even more stupid
things. That's just the way it works.
Then finally we adopted, as a rule, no new syntax. The reason for that is that new syntax
on old browsers is a syntax error, so we're trying to figure out a way to provide as much
value to people using the new language even on the old browsers as possible. If there's
a lot of new syntax, all that's going to do is frustrate you because it means that there
are powerful new things that the language can do but you cannot use as long as IE6 still
exists, and it's going to be around for a long time, so that doesn't actually do you
any good. We tried as much as possible to add powerful new capabilities to the language
without using syntax to accomplish that.
Despite that, we actually did add some new syntax. I recommend that you not use it, because
it will cause your programs to fail hard on all versions of IE before 9. But it's good
stuff, so let me show you what you're going to get that you can't use. This is mostly
in the line of corrections to the grammar, not new constructs.
First off, you now get trailing commas in literals. You can have an object literal with
a trailing comma. Some people get confused about how commas work -- they think they should
be delimiters rather than separators -- and now you can think about them either way. ES5
will ignore that trailing comma. It turns out trailing commas have always been legal
in arrays, but their interpretation differed. In most of the browsers a trailing comma is
ignored, but on IE it counts as an extra component. So the length of that array literal, on most
of the browsers, is 2; on IE it was 3, so that's going to clearly cause interoperability
problems. In ES5 it's going to be 2 everywhere, so we corrected that.
We fixed the reserved word policy with respect to properties. This used to give you lots
and lots of syntax errors; no errors anymore. It used to be that reserved words had to be
quoted in object literals, and you had to use subscript notation with quoted strings
instead of the dot notation. Now you can use the dot notation proudly everywhere; all legal
identifiers are permitted there. Again, you can't use this as long as there's IE6, but
someday your children will be able to use this.
We have getters and setters in the language now. Getters and setters define special properties
-- we call them accessor properties -- which instead of setting and retrieving a value
will call functions. Those functions can do whatever you want them to do. Generally they'll
set and obtain values. Here I've got a temperature constructor and it has two properties, Celsius
and Farenheit. You can set one and retrieve values from the other, and you'll always get
the stuff. It does conversions for you automatically. You can do very clean, very elegant, very
expressive APIs using getters and setters.
You also can do horrendous nightmare stuff. This is one of the places where you can very
easily get stupid. I expect we're going to see a lot of clever kids getting online saying
"look at the stupid things I did with get and set. I'll bet nobody thought we could
do that." Well actually we know you can do that, we're just hoping that you don't.
The problem with get and set is it breaks one of the invariants of the language. It
used to be that when you're accessing an object, either retrieving a value from a property
or setting an value in a property, you can be confident that nothing else in your world
is going to change while you're doing that. That's no longer the case. Now fetching a
value from an object can have side effects that effect things all over the system. That
makes the language a lot harder to reason about. Now you don't have to do that kind
of stuff and we're hoping that people won't; you can really uglify your world pretty significantly
with this feature.
We've got multiline string literals in the language now. We copied a number of other
languages that do this. I think this is a bad part. It wouldn't be ECMAScript if we
weren't adding at least one new bad part to the language, and I think this is it. I don't
like these for a number of reasons. One is it screws up indentation, because the continuation
line has to start at the left column, so it's just really ugly to read. But the other problem
is that there's a syntax problem. The first line is OK, and the second one has got an
error. Can anyone spot the error in the second statement?
>> AUDIENCE MEMBER: A space.
>> DOUGLAS CROCKFORD: What? Yeah, there's a space there. See, there's a space at the
end of the line. It's obvious once it's pointed out. But my philosophy is you don't want to
be using constructs that are difficult to distinguish from common errors, and this is
one of them. The alternative is just to have a lot of strings and plus them together. A
smart compiler will clean that up for you without any overhead; I think that's the better
way to do it. I don't think you get much value out of this.
These things are now constants. In ES3, strangely, they were variables, which means you can go
to the global undefined variable and set it to 5 and cause all the programs to break.
We fixed that in ES5; they're constants now, so if you change them that won't happen. Undefined
stays undefined. That's a good thing.
We fixed parseInt. ParseInt of the string 08 now yields 8 and not 0. It means on ES5
it's no longer necessary to provide the radix parameters, so that's nice. As long as ES3
is still in the world, as long as there's still IE6, you still have to use the radix
parameter because you might be running there someday.
Regular expression literals now produce a value every time they're evaluated, so that
corrects a bug in which you would only have one instance of the regular expression object,
which could cause problems if you had multiple uses of that using exec.
We cleaned up a problem where you can change object and array to your own functions. The
way that the ES3 spec was written it would always use your version of those, and there
are obvious security problems that came from that. We corrected that language in the spec
so that anytime you're creating new objects or arrays it is as if you were using the original
constructors so you'll always get the right stuff. That makes the language little bit
more resilient.
We added a lot of new methods to the language. It turns out adding new methods is a powerful
way of adding functionality without having breaking syntax, because no new syntax is
required for having new methods. For a lot of the new methods that we're defining it's
possible to implement them in JavaScript, which means that an Ajax library can add these
new methods to the primordial objects, which allows ES3 implementations to act exactly
like ES5 implementations except maybe a lot slower. That's a benefit which helps us have
compatibility going backwards and forwards. We don't feel bad about IE6 going a lot slower
than, say, IE9, because we really want to encourage people to move forward.
The first stuff you get is JSON. JSON is now built into the language; it is standard equipment
in JavaScript. No tricks, it's there. It uses the JSON2.js interface. If you're using JSON2.js,
note you're already set. No changes to your programs. When you get onto ES5 they'll just
run faster. JSON2 will get out of the way and allow the native interfaces to work.
By the way, stringify is my fault. I thought of that. I didn't want to call it toString
because it's a transformer, it's not a deserialiser. A lot of people don't like that name, but
that's what it is. We're stuck with that. My fault, sorry.
We've got a lot of other functions. If you're going to define these functions in your code,
I highly recommend that you first test to see if the function is already defined. If
it is, then don't do it. The technique that I've used for deciding that has changed over
the years. Originally I thought just doing a Falsey test was sufficient. That turns out
not to be the case. Then I was doing a typeof against a function. That turns out to not
work either; MooTools and some of the other Ajax libraries will screw you up. Now I call
hasOwnProperty to decide if I'm going to do that setting.
We now have the function bind method, which is something that's standard in a number of
the Ajax libraries. It's now built into the language. It's a currying function which takes
a function and returns a function which binds this and possibly some of the parameters that
you passed to it. It allows you to take methods and use them in places we expect a call back
or a simple function, so this is a very handy thing to have. It's now standard equipment.
String trim is now standard equipment in the language. Yes, you may applaud.
[applause]
Thank you. trim will take a string and remove the excess white space from the ends, which
is something we've always needed and now we've got it.
We've got a large set of array methods. These were worked out originally by Mozilla for
Firefox, and they're really good. The thing they all have in common is they take an array
and a function and they call that function for each element in the array, and they all
do something slightly different with that configuration. We took the API as Mozilla
specified it, and it was a bit of a problem because there are a couple of things I think
they got wrong in their API. But it was well established in the field so we felt that if
we repaired the API we'd probably break stuff and we didn't want to do that, so we took
it as is.
My complaints with this are first the optional this P argument. I think it's just unnecessary,
so I think it clutters the API. Much more significant is when it calls your function
it passes that function the current value, which is the right thing to do, it passes
it the subscript, which is all right, but it also passes the array itself, and I think
that's giving too much authority to that function. It didn't need that information. If it did
need it you could provide it to it. I think it was a mistake to have that delivered to
the functions implicitly. But we're stuck with that.
Despite that, these are really good functions. The every function is sort of an and function,
where it will call a function on each element of the array and as long as they return falsey
values it keeps going. Is it falsey or is it truthy? As long as they're true it keeps
going.
We've got filter, which I think is a more useful function. It's called on each element
and returns true or false. For all the ones where it returns true, it adds them to a new
array. It's a very nice way of selecting stuff out of an array.
We have forEach, which simply calls a function for each element. This is brilliant; I use
it a lot. There are some people who say don't use this, keep using forLoops because it's
faster. I think that's terrible advice. The speed difference is not significant, and the
benefit in the way you can express things and code is really profound. I'd highly encourage
use of these functions.
indexOf is like stringIndexOf in that you can find an element within an array based
on its value. lastIndexOf does it from the other direction. map is a useful function.
It's like forEach except for each of the values that came back, it puts them into an array
for you. That's a nice way of doing transformations on the elements of an array.
We have a reduce array, which calls a function for each value and takes that return value
from each one and passes it as an argument into the next one. You can use this for doing
sums, products, mins and maxes. It's a very useful thing. reduceRight does the same thing
from the other end of the array, and is not nearly as useful. some, which is the inverse
of every -- this is the or function which keeps going until it finds a false value.
Date.now. It used to be if you wanted to get the current time in milliseconds you had to
create a date object and then extract its value out, and that can take some time. That's
a problem because usually when you're using dates in this way you're measuring time, you're
trying to see how long your program is taking to run, so that gets in the way. Date.now
is standard equipment where it'll just return the thing very quickly.
We now have support for ISO dates in JavaScript. ISO dates are an international standard for
formatting dates and times. It's probably the best format we have, at least for data
processing applications, and it's now built into JavaScript. Using toISOString you can
get one of those forms and then new date and date.parse will take that as input.
One problem in ES3 was that it underspecified how date parsing worked. It pretty much left
that up to the implementers. It was proprietary; there was no guaranteed standard that would
work for date formats in all browsers. We now have a format that is guaranteed to work.
The browsers will first try to use the ISO format, and if that fails, they will then
try to use their proprietary formats.
We have Object.keys. This probably should have been Object.prototype.keys, but we were
afraid that if we put it on object.prototype it was very likely it was going to collide
with other applications and we didn't want to do that. We put it on the object object,
which was a little bit safer. We found there were still some collisions, like prototype
likes to mess with this stuff, but that's OK. They'll have to fix it.
What keys does is you give it an object and it returns an array containing all of the
own enumerable keys of that object. That turns out to be a really powerful thing. One thing
it means is you don't have to use forIn anymore which is really nice. One of the problems
with forIn was there was no guarantee as to what order the keys would come out. But getting
the keys all on one array, that means you can then call sort. You can say Object.keys.sort.forEach
and get a nice thing where they all come out in the order that you like. That level of
programming is now trivial to do; it used to be really hard.
We've got Object.create, which makes a new object which inherits from an old object.
Really powerful. This is the key operator for prototypal inheritance. It used to be
hidden in the language, but now it is first class in the language, so it's very straightforward
to make these things. One other nice thing you can't see from this because you only get
this in ES5, you can't simulate this in ES3, is you can call Object.create passing it null
as the prototype, and if you do that, you get an object that inherits nothing. That
turns out to be a really powerful thing. We'll talk more about that later.
It is finally possible to figure out if a value is an array in the language. This was
hidden for a long, long time, but it's now first class in the language in Array.isArray.
You pass that a value and it returns true if it actually is an array.
We have a powerful new meta object API in the language which gives you control over
the attributes and the properties of objects. OK, you know what objects are. Objects have
properties. We sometimes call them members or methods or elements, but they're all properties.
Properties have attributes. This goes all the way back from the original language, but
you could never see them. You could sort of detect them sometimes, but mostly they were
in a place where they were invisible to you. They had names in the original standard like
DontEnum and ReadOnly and DontDelete.
When those attributes were turned on then they could have that control over a property.
You could set a property to be read only and that meant that nobody could modify it, or
you could set a property to be don't enum which means it would not show up in the forIn
iteration, which would have been a really good thing if they had exposed it to you,
but they didn't. They specified it as a necessary thing for implementers to provide to the executing
environment, but they didn't expose it to the users of the language. There was this
frustration there where you wanted to set don't enum on your properties so they didn't
screw up your forIn loops but you couldn't. Now you can. We finally now have an API that
gives you access to this stuff. It turned out really, really nicely.
Before I show you the API, I need to explain that there are two kinds of properties now.
There are data properties, which are the kind of properties that you're used to, that we've
always had. They have a value that you can set or retrieve. And there are accessor properties.
Accessor properties are things that have get and set on them, the getters and setters.
They don't store a value, they just associate to functions with the property.
There are now six attributes in the language. The first four can be assigned to data properties,
and the last four can be assigned to accessor properties. Data property can have a value,
it can be writeable, it can be enumerable, it can be configurable. Accessor properties
can be enumerable, configurable, and they have a set function and/or a get function.
For the Boolean values you can set them to true or false. Once they get set to false
they can never be turned back to true. If you want something to be read only, you set
writeable to false and nobody can turn that back on again. That allows you to have some
integrity in your objects now. Writeable means that you can't modify it, can't modify the
value. Enumerable means it does not show up in forIn or in object.keys. Configurable means
that you can't delete it and you can't change it to another kind of property.
Let me show you an example of how we can set up a data property. You see two statements
here. They both do exactly the same thing. The first one is the way we would do it on
ES3, and it still works in ES5, but in ES5 we've got more options. You might look at
that and go holy cow, how is this an improvement? This is a whole lot of code to do what we
used to do with very little code. The interesting thing here is we have some options now that
we didn't have before.
For example, I don't have to inherit from Object.prototype. I can change that Object.prototype
to be a different object and we can inherit from somewhere else. And I now have control
over those attributes. I'm using a function here called Object.defineProperty. It takes
an object that we're going to inherit from or that we're going to modify, the name of
a property, and a descriptor object. A descriptor object is just an ordinary object but its
keys are the same as the names of the attributes that we want to set. Here I now have control,
finally, over writeable, enumerable and configurable. The default for object literals is to set
them all to false. The default for this form is to set them all to true. This one wants
to set them all to false, so you have to explicitly set them to true if you want them to be true.
We finally have control over those bits now, and that's a powerful thing.
We have a similar form for doing accessor properties. It's the same thing except now
we give it a descriptor object that's got get and set in it, which will be functions
that will do useful things. In this case I'm setting enumerable to be true, so it'll show
up in my forIn statement. But I did not set configurable to be true, so that means that
they will not be able to delete this guy or change him to have different getters or setters
or to have a different value.
This then is the new meta object API. We've got defineProperty, which you just saw. We
also have defineProperties, plural, which takes an object of descriptor so you can set
a lot of these at once. The object of descriptor has a key for each property that we want to
set and a descriptor, which is a separate object which describes what it's going to
do.
We also have functions which will retrieve these values for you. We've got getOwnPropertyDescriptor.
You give it a key and it gives you back one of these objects which describes the attributes.
There's also getOwnPropertyNames which is like keys except it returns all of the own
properties, even the ones that are not enumerable. We also have a getPrototypeOf function.
I'm recommending that you not use these functions because there are some security implications
to these, and secure frameworks may ban their use. You might wonder why we added things
to the language that you don't get to use. We added these specifically for Caja to allow
it to inspect the system in order to make it easier for it to lock the system down.
It's likely that what Caja will do with things like getPrototypeOf is it'll use it for its
own use and then delete it from the system so you can't get at it.
We also have control now over extensibility. There's an attribute in objects which controls
whether or not you can add new members, new properties, to that object. The key method
here is Object.preventExtensions. This sets that bit. Once you've set that bit you cannot
clear it again. Again, that will lock an object so that you cannot add new properties.
There are two other methods. One is Object.seal, which does the same thing except it also goes
through all the properties in that object and turns off their configurable bits. There's
also Object.freeze which does the same thing as seal except it also goes and turns off
all the writeable bits. You can freeze an object now and that makes it immutable; it
means that you cannot modify it or any of its properties. If any of its properties are
mutable themselves then you can still change them, but the top level of the thing is frozen.
It's not a deep freeze, it's a shallow freeze.
We also have methods which will return true or false on whether or not any of these things
have happened to the object. IsExtensible will be true if preventExtensions has been
called on it.
One of the things that's repaired by the new ES5 features is we've got a large class of
unintended inheritance problems. This is one of them. I want to count all of the unique
words in a text, so I create a word count object which will contain all of the words.
The keys will be the words themselves and the values will be the number of occurrences
we see. Then we'll pass our text to split, and split it on whitespace and punctuation,
and then we'll call forEach on the array that split returns, passing it a function which
will go through and count all the words it finds. The problem with this implementation
is that if one of the words in our text is constructor then it will fail in a really
unexpected way. That's because constructor is a word that's inherited by all objects
, and most of the time we don't notice that, but sometimes like in applications like this
it bites us.
Fortunately ES5 now provides us several alternatives for getting around that problem. One of them
is that we can use object.create null to create our word count object. That says that we want
this object to inherit nothing and means it will not have a constructor property, it will
not have a toString property, it will not have a hasOwnProperty property, and that means
that we don't have to worry about these collisions. It's guaranteed to be collision free.
For other classes of problems we can set the enumerable attribute to false when we're adding
methods or other values to the prototypes. That will also prevent accidental inheritance
in the case of forIn enumeration. We also now have object.keys which makes forIn completely
unnecessary. We can just get an array of keys and use the array methods for dealing with
those.
I'm now going to talk about the most important new feature in ES5, and that's the strict
mode. Strict mode is different from all of the other stuff I've talked about because
we're now talking about removing things from the language not adding things to the language.
JavaScript is a language which famously has a lot of bad parts, and we are limited in
our ability to remove the bad parts from the language. But by having a strict mode that
means that you are opting in, that you want the improved language which is obtained by
removing those bad parts.
The way you specify strict mode is with a pragma. The pragma is expressed as a use strict
expression literal which is either the first line of a file or the first line of a function.
We chose this kind of odd looking form because this is not a syntax error on ES3, so that
means you can now write ES5 strict programs and if you're writing in that intersection
they can run without modification on ES3. That's another technique for allowing us to
interoperate with the obsolete browsers.
There are two forms of it. There's the file form and there's the function form. The file
form means that use strict is the first statement in the file, and function form means it's
the first statement of a function. I highly recommend using the function form and not
the file form, and the reason for that is basically Steve Souders. It's because of Steve
Souders that you shouldn't use the file form. Is Steve still here? Yeah, it's your fault.
Why am I pointing at that man and saying it's his fault? Steve Souders observed that web
browsers are incompetent in the way that they deal with script files. They get very pessimistic
and tend to block, which slows down page loading, which deeply interferes with the user experience
and costs everybody time and money, and that's a bad thing. What's the solution to that problem?
Well we could go to W3C and insist that they fix the standards so that this kind of crappy
kind of behavior wouldn't be tolerated, but making stuff good is not what W3C does, so
that was not an option.
Instead Steve decided to fix it himself by proposing some rules. Rule number one is reduce
your number of HTTP requests, and the way you do that with script files is you take
all of your script files and concatenate them together into one big file. It works. If you
follow Steve's plan and follow rule one and the other rules, you can significantly improve
the time that it takes to start your page, and that's a very, very good thing. Mission
accomplished, case closed, done.
The problem is you now have things which used to be separate and modular all mushed together,
and in the case of the use strict pragma it means that the first file in the stream of
files decides the strict mode for everything that follows. If the first file has strict
mode in it then all the files that follow will have to be strict. If they were not designed
to be strict they will fail, and we've already seen sites experience this problem. Or it
could be the other way: the first file is not strict, and that means strict mode does
not get turned on for anything else. That's another source of problems.
It would have been better to fix the browsers. I think it is still better to fix the browsers,
but that doesn't seem likely. For that reason, I recommend you use the function form because
in the function form the use strict pragma is scoped to that function, so everything
inside of that function will be strict including its inner functions, but nothing outside of
it will be strict. That way we can concatenate files together and we won't have these problems.
One of the things that happens with strict mode is that there are new reserved words.
You cannot name variables with these words. That's because future editions may require
these words. It's not guaranteed that they will, but they might.
Strict mode has a lot more rules and restrictions on the way it executes code. These are important
so I'm afraid I have to read through this list. There are going to be no more implied
global variables within functions. You'll get errors on those now. You don't have to
save your applause to the end, you can clap spontaneously.
When you call a function as a function, this will no longer be bound to the global object,
this will be undefined instead, which is better. Apply and call do not default to the global
object or try to wrap their this parameter in an object wrapper, you just get the value
that you passed.
No more with statement. In the future, there'll be no with statement in JavaScript. If you
try to set a property that has been set to not writeable, you will throw. In ES3 you
get a silent error, which is the worst kind of error. Death before confusion is my motto,
so at least in this case the program's not going to be confused by thinking that it set
a value and keep running having not set that value. A similar thing happens if you try
to delete a property that is not configurable.
There are restrictions on eval now. Eval does not get access to the function's context,
so you cannot create variables, you can't violate the security of the function that
called eval. That's great. Eval and arguments now act as reserved words, so you can't have
variables with those names.
Arguments is no longer linked with the parameters. I don't know if you're aware of this, but
if you change one of the arguments that you were passed, a similar change happens in the
arguments array. If you take the arguments array and shift it, all of your parameters
will shift. It's insane. It really is goofy, and it's also one of the things that makes
JavaScript really hard to accelerate. By having this restriction in strict mode not only do
arguments get a lot more rational but it'll be easier for the JavaScript engines to go
fast.
There's no more arguments.caller or arguments.callee. This was a bit tricky to specify because the
standard never had arguments.caller so we couldn't delete it from the standard because
that wouldn't have any effect. What the standard does now is it says the implementation in
strict mode has to set them to null, so that was weird.
No more octal literals. We haven't needed octal literals since Fortran, and somehow
they keep finding their way into modern programming languages. We're trying to stamp them out,
so in strict mode octal literals go away. It's now a syntax error for an object literal
or a function parameter to be duplicated. This should have always been the case, and
now it is.
Some good things come from these rules. One is that forgetting to use the new prefix will
now throw an exception rather than silently clobber the global object, so that's good.
Again, death before confusion. We should be putting this on t-shirts and writing it on
the walls. It should be our new slogan.
Strict mode will break things. You have to be careful when you opt into it, because not
everything's guaranteed to work. In fact, some things are guaranteed to break, and that's
by design. That's what strict mode is supposed to do. One case to watch out for is calling
addEventListener as a function is probably going to fail from now on. The reason is in
the old days when you called something as a function this would be set to the global
object, so addEventListener happened to get window, which is the browser's name for the
global object. That doesn't happen anymore. In ES5 this will be set to undefined, so addEventListener
tries to add an event to undefined and will fail.
The correct way to write it -- and this works in ES3 too, so there's not a compatibility
problem necessarily -- is to put the window. prefix on it. Now it's being called as a method,
now this will be bound to the global object, and it will work. There are a lot of those,
but this is probably the first one you're going to run into.
There are no methods in strict mode for determining if you're actually in strict mode or if it's
possible to go into strict mode, but fortunately it's really easy to figure out. In strict
mode it simply calls a function immediately and looks at what this was and negates it.
If it was the global object it turns into false, and if it was undefined it'll turn
into true. Then if you want to find out if strict mode is possible in your system, you
do the same thing except you make the function that you're calling be strict. It'll then
follow the right rules if it knows how. If you're in a system that doesn't know about
strict mode then the use strict pragma is ignored and the program keeps going.
If you're using JSLint chances are you're going to have a really good time converting
to strict mode. You still need to test, obviously, in strict mode browsers and that's becoming
possible now. There's some stuff that JSLint will not find because JSLint does a static
analysis and much of the new strict mode rules are dynamic. There are things that JSLint
can't find, but it can find a lot of the problems for you.
Why do we care about strict mode? It's because of mashups. Mashups are an important mode
of software development. It's something that people have been working on for decades and
it sort of happened in JavaScript with virtually no effort. It's just that JavaScript happens
to have these amazing set of properties that made it easy to do mashups, whereas in other
languages it was still an unsolved problem. Mashups are really important but we have the
problem that the security model in the browser did not anticipate mashups, so it's not possible
for a page to defend itself against the third party scripts that are running inside of it.
The way we've been dealing with that at Yahoo! is with safe JavaScript subsets, where we
try to constrain the power that the third party code will have so that it cannot take
over the page or corrupt the user or do any of those things. There are lots of versions
of this idea. The one that we use at Yahoo! is Caja, and there's also my own development
ADsafe. Caja and ADsafe are based on the same theory, but took completely different approaches
to how they implemented it.
Caja does a JavaScript to JavaScript translation, and in that process it adds a lot of runtime
checking and redundancy and indirection to the program in order to allow it to have confidence
that the program cannot get out of control when it's executed. One difficulty with that
approach is it adds complexity to the development process because the code that you're debugging
may not look anything like the code that you wrote. It can also slow the script down pretty
significantly, which hurts performance and we're trying hard to make things go faster,
not slower.
ADsafe takes a different approach. It uses static validation. It looks at a program and
as long as it can determine statically that it doesn't use global variables and doesn't
use facilities in the language which would allow it to get access to global variables
then it's safe. ADsafe has no translation and no runtime penalty, which is great, but
it uses a greatly restricted subset of the language. This is not allowed and the subscript
operator for all purposes is not allowed either. It turns out you can still write good programs
in the subset that's left, because function is in that subset and you can do great things
with function, but it's more difficult to use.
With ES5 Strict we can get the benefit of both. We can get the larger language that
we get with Caja with none of the runtime penalties, because we can do everything with
static validation. ES5 Strict gives us enough control over the browser's environment that
we can statically determine that a program's going to be safe. With a little bit of library
assistance, we can have third party code running in the browser in a fully cooperative mode
without any degradation. That's a great thing. That's why this is so powerful, because security
is the biggest problem that we have in web applications.
But then there's the IE6 problem. Again, you don't have to hold your applause to the end
if you don't want to. IE6 used to be the best web browser in the world. It was for many
years, but it's not any more. It's way past its expiration date, but it's still one of
the most popular web browsers in the world. Its numbers that we see are still consistently
bigger than Opera, Safari and Chrome combined, so it's hard to ignore this monster. It's
way out of date. It does not run ES5; you get none of the benefit of ES5 until we kill
this thing. That's a problem, that's a big problem.
IE7, yeah. 8. Now this one breaks my heart: 9. I'm really unhappy to report this. I'm
impressed with the things that the IE team at Microsoft has done with IE9. I thought
they were wedged, I did not think it was possible any more for them to make a good browser with
all the legacy problems they've got and everything. They've done some really good stuff in IE9,
but they made one really huge blunder: somebody at Microsoft decided that it was not important
to implement strict mode. I think they did a survey of some developers and asked them
if they need strict mode, and they made a point to ask developers who didn't know what
it was. They said no, I guess not. So they went good, we don't have to do it.
It turns out the standard has no provision for subsetting in it, so you're either standards
compliant or you're not, and Microsoft chose to be non-compliant. The reason they give
for that now was they just didn't have time, or they were short staffed, they couldn't
find somebody to do it. I think Microsoft employs more programmers than any other organization
in the world, and they couldn't find somebody to implement strict mode. That's what they
say.
They were not blindsided by strict mode. Microsoft was on the committee that created this. A
Microsoft representative was one of the principal architects of strict mode; he was the editor
of the standard, did a brilliant job on the standard. It's the best web standard in the
history of the web. Microsoft voted yes at the General Assembly for this, so it's not
like they didn't see this coming.
The IE team didn't see it coming. The IE team didn't understand the importance of JavaScript
to the browser. The reason for that is it's an organizational problem at Microsoft. The
Jscript team is over here with dev tools and I think Windows is over here with operating
systems, and to find the lowest common manager: Steve Ballmer. So the IE team didn't know
this was important, didn't know to ask the tools teams "we need this, get it done." Once
they realized the blunder, they just dug in and said well, we'll just tell people we didn't
have time to do it right.
So IE9 is shipping without strict mode in it, which means that we really don't get the
benefit of strict mode until IE9 is dead. I hate that. I think Microsoft has done a
terrible thing to the web community in doing this. Will they get it right in IE10? I don't
know. They have not announced IE10 yet so we don't know what's in it. I'm hoping that
they're paying attention and that they'll get this right. This is a big disappointment.
So what's next? That's ES5. It took about an hour to describe everything in it -- not
a lot there but it's good stuff. It gives you much more control over the language. I
don't expect everybody's going to use the meta object API in their applications -- in
fact I think you shouldn't -- but it makes a lot of sense for the libraries to use. They
can now make better objects.
What's in the next edition? I don't know at this point. The committee is still trying
to work that out. There are some things which look likely but could change, so don't start
coding to any of this, but this is some of the stuff that I think we might see. I think
we may see lighter and more expressive syntax. We might see comprehensions. I think we might
see that those stupid parentheses around the if statement... It turns out there's no need
for those; you've got if on one side and the curly brace on the other, and that's a Fortran
thing. We don't need that. Maybe those will go away.
The language might get lighter. Maybe we'll come up with a better alternative to semi-colon
insertion; that would be nice. We'll probably be seeing more expressive literals. The stuff
that I showed you that you could do with the meta object API, it's possible that you might
in the future be able to do those directly at object literals, that annotations will
be there so you can do it more easily.
A feature that I'm very much looking forward to is proper tail calls, tail recursion, which
means that when the last thing that a function does is return the value obtained from calling
another function, when we generate code for that instead of doing a call and a return
we can just do a jump. That can give us enormous performance benefits, and even more importantly
it opens up whole new forms of programming. We can do continuation passing style programming,
we can do monads and arrows and all these wonderful new functional construction things.
This is going to push the language to a really interesting new place; I'm very much looking
forward to that.
We'll probably have modules. Modules I think will be the most important feature of the
next edition. We'll finally get rid of the shared global object, which is the source
of the cross site scripting problem. We'll finally have good interfaces in the language
so that you can define a module and it can declare its interfaces and decide for itself
how much of its guts it wants to expose to the rest of the community. That'll be a really
good thing; that'll go a long way to fixing our security problems.
We'll have proxies in the language. Proxies are like catch-alls; they allow you to muck
with the inner wiring of objects and control their behavior at a level that's way more
intricate than you can do now. It's like getters and setters except a thousand times worse.
You can really do terrible ugly stuff with proxies.
Now, the benefit of proxies is that they will allow us to fix the DOM. We can put a proxy
perimeter around the DOM so that we can control the security of the DOM without having to
re-implement the whole thing, which is the way we have to do it now and it's really expensive.
A better way of fixing the DOM would be to have W3C fix the DOM, but that's not going
to happen, so at least the ECMAScript committee is on the right page with respect to this
stuff and is dealing with the important problems first. Proxies, in my view, are not an ideal
solution but at least it's a solution and that's better than what we're getting.
We might get around to fixing the typeof operator so that typeof null will be null. Does anybody
like that? Yeah?
And much, much more. Probably way too much, much more. Committees love putting new stuff
in. This is already, I think, a really long list, and it goes on way, way longer than
this. I'm hoping that the committee can find the will to keep it small. The difficulty
in doing that is all of the features that we're considering are really interesting and
probably valuable, but the sum of all of those features I think is going to be excessive,
so we're going to have to make some hard tradeoffs in deciding what to put in and what not.
That's all I've got for you tonight. I'll look for you next time when we get to Section
8. Thank you and good night.
[applause]