Restrict mode for JavaScript (ECMAScript) is a proper subset of the language. Conform to it and enjoy more robust programs.
The case for restrict mode (announcement)
Discuss restrict mode in the JSShaper google group
Try restrict mode directly in your browser
Restrict mode is simple to learn. The rules are:
===
and !==
===
and !==
are
consistent, you'll remember their semantics, and most of the time
they're exactly what you wanted. The loose ==
and !=
operators are quite complex and their
semantics are not very consistent, but they are still available in
restrict mode. The use of the built-in strict operators is a
common practice today. It's a pity there's just two of them. But
we're fixing that now.< <= >= >
strings
or numbers
(but never a
mix)
click for why?
< >= >= >
either
compares numbers
or strings
(lexically). Mixing these types yields unrobust programs. Relying
on automatic coersion from other types may surprise you. Use
the String()
or Number()
functions
(without new
) to type-convert explicitly, when you
need to.[1] < [2]
, [1,2] < [1,3]
, but [10]
< [2]
(coerced into "10" < "2"
)
var x = new Number(3), y = new Number(3); x
<= y, x >= y, 3 == x, 3 == y, but x != y
. The first comparisions coerce
to primitives, the last compares object identity.
numbers
and strings
may
surprise you, especially so when mixed by mistake: 01 <
1 === false, "01" < 1 === false
but "01" < "1"
=== true
. The first two compare (coerced) numbers, the
last strings (lexically).
+
strings
and numbers
(in any combination)
click for why?
+
is such a
common source of bugs. a + b
either adds
numbers
or concatenates strings
. Relying
on automatic coersion from other types may surprise you. Use
the String()
or Number()
functions
(without new
) to type-convert explicitly, when you
need to. For string
formatting, you're often better
off
using Fmt()
or something similar.+
. JS coerces into strings
and
then concatenates those which is confusing and almost never
what you want: [1, 2] + [3, 4]
yields "1,23,4"
.
num + undefined
or str + undefined
is rarely what you intended so an informative error is more
useful than a bogus NaN
or "...undefined"
value.
nostr_nonum + nostr_nonum
are
complex and error prone. What's the result of obj1 +
obj2
?
toString()
and valueOf()
ordering
may surprise you: "object is " + {toString: function() {
return "str"; }, valueOf: function() { return 1; }}
yields "object is 1"
.
Date
is not funny: (new Date()) + 1 === "Tue Dec 28 2010 23:59:53 GMT+0100 (CET)1"
but
(new Date()) - 1 === 1293577193075
. It coerces
to string
or number
depending on
operator. Use ES5 Date.now()
when you just need
the timestamp.
num + str
(and str + num
) for
pragmatic reasons. It happens to be the most common coercing
usage out there in the wild. Allowing this is a trade-off
between slighly increased complexity vs lowering the barrier of
restrict mode adoption.
+=
.
- * / % & | ^ ~v << >> >>> -v ++v v++ --v v--
numbers
click for why?numbers
, per definition. Relying on automatic
coersion from other types may surprise you. Use the
Number()
function to type-convert explicitly, when
you need to.null * 2 === 0
but undefined * 2 ===
NaN
. Both cases are common sources of bugs. That they
yield different results is interesting in itself since JS
defines null == undefined
.
valueOf
and toString
functions. Both must return primitive
values (but not necessarily number
or string
). The order of how these are invoked is
not obvious: "o: " + {valueOf: function() { return 3; },
toString: function() { return "4"; }} === "o: 3"
. In
practice, valueOf
isn't used much other than for
automatic unboxing
of Booleans
, Numbers
and Strings
. For the rare times when you use boxed
types you can easily convert them to primitives explicitly. It's
crucial that you know when you use boxed values (and limit their
usage), since their semantics are sometimes surprising. Such
as: Boolean(new Boolean(false)) === true
.
-= *= /= %= &= |= ^= ~=
<<= >>= >>>=
.
+v !v && || ?: o[k]
and all other
operators have identical semantics in restrict mode
click for why?+v
(unary plus) is defined to type-convert into number
,
but you should consider using Number(v)
instead. !v
is defined to
type-convert into boolean
, and then inverts it. && ||
?:
compares against falsy values (so does the if
-statement and
others). o[k]
(index) converts its key
to string
. And so on. I could have missed out on
something that should be restricted, please let me know in that case.
typeof
, such
that typeof null
is disallowed. Let me know if you
have an opinion on that.
Get started with restrict mode by
downloading JSShaper,
a JS/ES source transforming framework. It includes
the restricter
plugin, which adds checking to your
program without destroying your formatting. Run your tests on the
output of restricter
. Since restrict mode is a proper
subset of JS, and conversely JS is a proper superset of restrict mode,
your restrict mode conforming programs works fine in any JS
engine. Only that by running your program
through restricter
now and then, it becomes more robust.
click for instructions
git clone git://github.com/olov/jsshaper.git
cd jsshaper
git submodule init
git submodule update
cd src
node run-restricter.js myfile.js > myfile-checked.js
# then include plugins/restricter/restrict-mode.js in your project
Like ES5 strict mode, restrict mode is opt-in. Similarly, you enable
it by placing "use restrict";
first in your program or
function. There's always the option to bail out: (/*@loose*/ "1"+2+3)
.
In fact, chances are that you are already mostly conforming to
restrict mode, except that you may use str + nostr_nonum
to
perform string conversion and concatenation at the same time. You may
find that replacing that
with Fmt()
or String()
calls here and there is easy. You may also
find that restrict mode conforming programs are easier to read and
reason about.
You're encouraged to "use strict"; "use restrict";
This isn't about fixing you, it's about fixing the language. Even the best make mistakes due to loose semantics. If you're not convinced yet about loose operator complexity, take a look at this implementation of EcmaScript operators in EcmaScript. Also note that SpiderMonkey, JSC and V8 does not implement identical semantics. Since restrict mode is opt-in, you may consider adding it just to a part of your program.
Restrict mode was created by Olov Lassus and isn't related to the work by the ECMA committee. ES5 strict mode was important in that it started improving the language by reducing it, not just adding to it. Restrict mode, just like ES5 strict mode, is something you can start using today thanks to JSShaper. It's technically possible, but not very likely, that a JS engine could add built-in support for restrict mode in the future. It doesn't really matter.
Restrict mode is a moving target and details are likely to change based on your feedback. Thanks for trying it out!
Follow me on Twitter