Previously I analyzed ECMAScript 5’s Object and Property system. This is a huge new aspect of the language and deserved its special consideration.
There are a number of other new features and APIs that need attention, as well. The largest of which are Strict Mode and native JSON support.
Strict Mode is a new feature in ECMAScript 5 that allows you to place a program, or a function, in a “strict” operating context. This strict context prevents certain actions from being taken and throws more exceptions (generally providing the user with more information and a tapered-down coding experience).
Since ECMAScript 5 is backwards-compatible with ECMAScript 3, all of the “features” that were in ECMAScript 3 that were “deprecated” are just disabled (or throw errors) in strict mode, instead.
Strict mode helps out in a couple ways:
Most of the information about strict mode can be found in the ES5 specification [PDF] on page #235.
It should be noted that ECMAScript 5’s strict mode is different from the strict mode available in Firefox (which can be turned on by going to about:config and enabled javascript.options.strict). ES5’s strict mode complains about a completely different set of potential errors (whereas Firefox’s existing strict mode tries to enforce some good practices, only).
How do you enable strict mode?
Simple. Toss this at the top of a program to enable it for the whole script:
Or place it within a function to turn on strict mode only within that context.
Note the syntax that’s used to enable strict mode (I love this!). It’s simply a string in a single statement that happens to contain the contents “use strict”. No new syntax is introduced in order to enable strict mode. This is huge. This means that you can turn strict mode on in your scripts – today – and it’ll have, at worst, no side effect in old browsers.
As you may note from the examples here and in the previous post there are virtually no new syntax additions or changes to the language in ECMAScript 5. This means that you can write your ES5 scripts in a manner that will be able to gracefully degrade for older useragents – something that wasn’t possible with ECMAScript 4. The way in which strict mode is enabled is a great illustration of that point in practice.
A neat aspect of being able to define strict mode within a function is that you can now define complete JavaScript libraries in a strict manner without affecting outside code.
A number of libraries already use the above technique (wrapping the whole library with an anonymous self-executing function) and they will be able to take advantage of strict mode very easily.
So what changes when you put a script into strict mode? A number of things.
Variables and Properties
An attempt to assign foo = "bar";
where ‘foo’ hasn’t been defined will fail. Previously it would assign the value to the foo property of the global object (e.g. window.foo
), now it just throws an exception. This is definitely going to catch some annoying bugs.
Any attempts to write to a property whose writable attribute is set to false, delete a property whose configurable attribute is set to false, or add a property to an object whose extensible attribute is set to false will result in an error (these attributes were discussed previously). Traditionally no error will be thrown when any of these actions are attempted, it will just fail silently.
Deleting a variable, a function, or an argument will result in an error.
Defining a property more than once in an object literal will cause an exception to be thrown
eval
Virtually any attempt to use the name ‘eval’ is prohibited – as is the ability to assign the eval function to a variable or a property of an object.
Additionally, attempts to introduce new variables through an eval will be blocked.
Functions
Attempting to overwrite the arguments object will result in an error:
arguments = [...]; // not allowed
Defining identically-named arguments will result in an error function( foo, foo ) {}
.
Access to arguments.caller
and arguments.callee
now throw an exception. Thus any anonymous functions that you want to reference will need to be named, like so:
The arguments
and caller
properties of other functions no longer exist – and the ability to define them is prohibited.
Finally, a long-standing (and very annoying) bug has been resolved: Cases where null or undefined is coerced into becoming the global object. Strict mode now prevents this from happening and throws an exception instead.
with(){}
with(){}
statements are dead when strict mode is enabled – in fact it even appears as a syntax error. While the feature was certainly mis-understood and possibly mis-used I’m not convinced that it’s enough to be stricken from the record.
The changes made in ECMAScript 5 strict mode are certainly varied (ranging from imposing stylistic preferences, like removing with statements, to fixing legitimately bad language bugs, like the ability to redefine properties in object literals). It’ll be interesting to see how people begin to adopt these points and how it’ll change JavaScript development.
All that being said, I’m fairly certain that jQuery is ES5-Strict compatible right now. Once an implementation of the language is made available (so that that premise may be tested) I’ll happily switch jQuery over to working exclusively in strict mode.
The second major feature of the language is the addition of native JSON support to the language.
I’ve been championing this move for a long time and I’m glad to see it finally arrive in a specification.
In the meantime PLEASE start migrating your JSON-using applications over to Crockford’s json2.js. It is fully compatible with the ECMAScript 5 specification and gracefully degrades if a native (faster!) implementation exists.
In fact, I just landed a change in jQuery yesterday that utilizes the
JSON.parse
method if it exists, now that it has been completely specified.
There are two primary methods for handling JSON: JSON.parse
(which converts a JSON string into a JavaScript object) and JSON.stringify
(which convert a JavaScript object into a serialized string).
JSON.parse( text )
Converts a serialized JSON string into a JavaScript object.
JSON.parse( text, translate )
Use a translation function to convert values or remove them entirely.
JSON.stringify( obj )
Convert an object into a serialized JSON string.
JSON.stringify( obj, ["white", "list"])
Serialize only a specific white list of properties.
JSON.stringify( obj, translate )
Serializes the object using a translation function.
JSON.stringify( obj, null, 2 )
Adds the specified number of spaces to the output, printing it evenly.
JSON.stringify( obj, null, "\t" )
Uses the specified string to do the spacing.
Additionally, a few new generic methods have been added to some of the base objects but, frankly, they aren’t that interesting. The results from String, Boolean, and Number are just equivalent to calling .valueOf()
and the result from Date is equivalent to calling .toISOString()
A welcomed addition to the language is a built-in .bind()
method for enforcing the context of a function (virtually identical to Prototype’s .bind implementation).
Function.prototype.bind(thisArg, arg1, arg2....)
Enforces the ‘this’ of the specified function to a specific object – and passing in any specified arguments.
Considering how long this function (and its equivalents) have been around it’s a welcome addition to the language.
Dates are now capable of both parsing and outputting ISO-formatted dates. Thank goodness, about time. rimshot
The Date constructor now attempts to parse the date as if it was ISO-formatted, first, then moves on to the other inputs that it accepts.
Additionally, date objects now have a new .toISOString()
method that outputs the date in an ISO format.
A native, built-in, .trim()
is now included for strings. Works identically to all the other trim methods out there – with the potential to possibly work faster.
Steven Levithan has discussed the trim method in great depth.
The JavaScript Array Extras that’ve been around for, what seems like, forever are finally formally specified. This includes the following methods: indexOf, lastIndexOf, every, some, forEach, map, filter, reduce, and reduceRight.
Additionally a new Array.isArray
method is included, providing functionality very similar to the following:
Altogether I think ECMAScript 5 makes for an interesting package. It isn’t the massive leap that ECMAScript 4 promised but it is a series of respectable improvements that reduces the number of obvious bugs while making the language safer and faster. I’m looking forward to when some implementations start to go public.
Posted: May 21st, 2009 -->
If you particularly enjoy my work, I appreciate donations given with Gittip.
Greg (May 22, 2009 at 7:12 am)
I’m not clear on why it’s a good thing that arguments.caller and arguments.callee are deprecated/disabled. Could you explain? I thought they were useful and neither ambiguous nor error-prone.
John Resig (May 22, 2009 at 7:26 am)
@Greg: I did some digging and found a thread where they discuss this issue. It seems like the biggest problem is passing the arguments object to another function, where it is capable of accessing the callee/caller information, without your knowledge. Personally, I’m going to miss
arguments.callee
, I used it in a number of places in my code.Harley Jones (May 22, 2009 at 7:54 am)
@Greg, @John: I’ve been using arguments.caller and arguments.callee to create a stack trace for error handling. Can this be accomplished another way? I suppose I could use a block of code outside the “use script” scope. Is there a “stop strict”?
John Resig (May 22, 2009 at 8:00 am)
@Harley: No, it doesn’t appear as if there’s any way to break out of strict mode, once you’re in it. Your specific use case seems fairly safe since it’s a library that would be included during the debug process, so you could ask them to disabled strict mode explicitly.
Kevin van Zonneveld (May 22, 2009 at 8:09 am)
Thanks for the heads up John, this looks awesome!
Joe Larson (May 22, 2009 at 8:16 am)
I keep hearing various versions of this: “No new syntax is introduced in order to enable strict mode. This is huge. This means that you can turn strict mode on in your scripts – today – and it’ll have, at worst, no side effect in old browsers.”.
Except that, as soon as I start writing code that expects to be running in a strict context, that code may not fail in a ES3 browser, but it sure won’t do what I want. Some of the degradation will be graceful– inability to lock down properties, etc, we have to live with that today. Other degradation is not so graceful– for example I can’t write code that relies on the enumerable setting for a property being on or off. Which means I will either have to say “only ES5 browsers” or have two sets scripts or have one set of scripts with lots of switches. This seems painful. What am I missing?
John Resig (May 22, 2009 at 8:26 am)
@Joe Larson: I think you misinterpreted what I said – I didn’t say that all ECMAScript 5 code is backwards compatible to ECMAScript 3 – just that the syntax is backwards compatible. You’ll be able to use feature detection to determine if the newly-added features work as you expect them to and be able to provide a graceful fallback if they don’t.
Now, if there was absolutely no new logic introduced in ES5 then that would be a sad language update indeed – going nowhere very slowly.
Having two versions of a script seems fine to me, if you’re taking explicit advantage of some of the advanced features (like
Object.defineProperty
) AND you MUST continue to support all browsers – since any sort of “graceful” degradation there simply isn’t possible – you’d be under an inferior runtime.Strict mode is one thing that we can start to use immediately in all browsers (especially since it’s mostly useful as a debugging tool). We’ll have to wait a bit before we can use the others everywhere, as well. That being said – for those working in a single environment (developing Firefox extensions, writing ActionScript, iPhone applications, or other single-runtime applications) will receive an immediate, and large, benefit from this language update.
Joe Larson (May 22, 2009 at 8:38 am)
John — ok, thanks for the clarification. It just seems like the language being used keeps alluding to some kind of magical backwards compatibility, and as you rightly say, that is sort of nonsensical for a real language upgrade. As long as the pain is acknowledged, that’s fine, two scripts is worth it. And hopefully we don’t have to live with that for long, or at all in some circumstances.
Andrea Giammarchi (May 22, 2009 at 9:16 am)
arguments.callee … for god sake do not touch it!
I cannot imagine a global scope where every single function will persist causing naming conflicts everywhere because of this pointless choice!
setTimeout(function a(){}, 1);
alert(this.a);
In every Internet Explorer above code will create the CHAOS … it seems that everything is Douglas Crockford choice ( arguments.callee as an error ) and YUI Compressor ( with( … ) ) these two things will make backward compatibility in strict mode a nightmare.
Finally, again vice-versa project, Array is now 1.8 almost full specs.
Mariusz Nowak (May 22, 2009 at 9:20 am)
What is your recommended way of implementation this ECMAScript 5 features in ECMAScript 4 engines? Should libraries edit native objects and set those methods on them e.g.:
if (!Array.isArray) {
Array.isArray = function (obj) { ... }
}
Or maybe we should keep away from that. Some other js code on webpage may do some sniffing based on existence of those methods. If we put them in with our library then “other code” may not work as expected – this is of course rare case and doesn’t say much good about “other code” but still – should we care about that ?
How do you deal with that in jQuery ?
Nick (May 22, 2009 at 9:24 am)
I may well be missing something but what is the difference between .bind() and .call()?
TNO (May 22, 2009 at 9:24 am)
@Greg and John Resig:
The arguments object in general is being set up to be removed from the language and replaced with named, optional and rest parameters instead.
arguments.callee can be replaced with a named function literal:
var foo = function self(){
...
self()
}
“self” will exist only in the scope of that function, not globally, so this will be shorter code that arguments.callee.
As for arguments.caller, I don’t believe that was ever part of the spec anyway… Plus it seems that the next version (after ECMAScript 5) should provide separate stack tracing functionality (possibly on the Error object)
Andrea Giammarchi (May 22, 2009 at 9:35 am)
@Mariusz Now