ちょっと前に、株式会社LIGのブログでundefined
はただのグローバル変数だからvoid 0
を使うほうがよいみたいな記事がありました。
いろいろ調べてみると、それだけが理由ではないように思えたので、そこらへんをまとめてみました。
1行でまとめると次のようになります。
undefined
との比較判定には、やっぱりvoid 0
を使うこと (ほとんどのブラウザで最速だから)
もうすこし細かい話をすると、
- ECMAScript 5では
undefined
に上書きできない- グローバルオブジェクトの
undefined
のプロパティ属性[[Writable]]
がfalse
のため
- グローバルオブジェクトの
みたいな感じです。
速度比較
まず、次のグラフをご覧ください。これはJavaScriptのstrictモードにおいて、undefined
を比較するときに、どのようにしたら処理が速いのかをブラウザごとに比較してみた結果です。
グラフ横軸は1秒間あたりの実行回数なので、右にいくほど良い値となります。
サンプルが少ないので参考程度に見たほうがよい気がしますが、Chrome 30を除いてvoid 0
を利用したものが一番速い結果となっています。
なお、Google Closureコンパイラではundefined
をvoid 0
に変換するらしいので、それも含めるとvoid 0
とするのが最善な気がします。
undefined
は値を変更できない
undefined
はただのグローバル変数なのは間違いありませんが、ECMAScript 5からは値は変更できなくなっています。
次の例はJavaScriptコンソールで実行してみた結果です。
> window.undefined undefined > window.undefined = "foobar" "foobar" > window.undefined undefined
値の変更ができないことが示されているのが、ドラフトの「Annex E: Additions and Changes in the 5th Edition that Introduce Incompatibilities with the 3rd Edition」の15.1.1です。
15.1.1: グローバルオブジェクトの値
NaN
,Infinity
,undefined
は読み込み専用プロパティに変更された。
確かにECMASCript 3までは変更可能ですが、個人的には今さらな感じがしますので、void 0
を利用するのは、上で紹介した速度面の理由が大きいと思いました。
プロパティ属性 [[Writable]]
ここから、undefined
が値を変更できない理由を調べていきます。
ドラフトの15.1.1は、「15.1.1 Value Properties of the Global Object」で、その中にundefined
の記述があります。
15.1.1.3
undefined
undefined
の値はundefined
である。このプロパティは属性{ [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }
を持つ。
2重角カッコ[[ ]]
はプロパティの属性で、これは「8.6.1 Property Attributes」のTable 5で示されているものです。
[[Writable]]
(ブール値)
false
なら、ECMAScriptコードによる[[Put]]
での[[Value]]
属性の変更は成功しない。
[[Put]]
はオブジェクトのプロパティを変更するための、オブジェクトの内部プロパティ(8.6.2 Object Internal Properties and Methods)で、これが失敗するというわけです。
ちなみに、配列のlength
プロパティは値が変更可能です。
> a = [1,2,3] [1, 2, 3] > a.length = 5 5 > a [1, 2, 3, undefined × 2]
これは、length
プロパティが[[Writable]]
がtrue
だからです。
15.4.5.2 length
length
は初期に次の属性を持つ:{ [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }.
defineProperty
ECMAScript 5から導入されたObject.definePropertyを利用すれば、
[[Writable]]
などのプロパティ属性を指定することができます。
次の例ではobj.aの値を99に指定したと同時に変更不可能にして、それを確認しています。
> obj = {a:0} Object {a: 0} > Object.defineProperty(obj, "a", { value: 99, writable: false } ); Object {a: 99} > obj Object {a: 99} > obj.a = 1 1 > obj Object {a: 99}
おわりに
個人的にはx === undefined
が大好きです。
本当に高速化したければClosureなどを使うなりすればよいので、void 0
は知識として知っておいたほうがよいが、自分では使わないかなあと思いました。
ちなみに、jsPerfのベンチマークは誰でも実行できて結果が共有されるようになっていますので、より正確な値を得るために、みなさんもいろいろなブラウザや環境で試していただけると嬉しいです。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。