2013/07/11

JavaScriptでundefinedよりvoid 0を使ったほうがよい理由はほとんどのブラウザで最速だから、かも

ちょっと前に、株式会社LIGのブログundefinedはただのグローバル変数だからvoid 0を使うほうがよいみたいな記事がありました。

いろいろ調べてみると、それだけが理由ではないように思えたので、そこらへんをまとめてみました。

1行でまとめると次のようになります。

  • undefinedとの比較判定には、やっぱりvoid 0を使うこと (ほとんどのブラウザで最速だから)

もうすこし細かい話をすると、

  • ECMAScript 5ではundefinedに上書きできない
    • グローバルオブジェクトのundefinedのプロパティ属性[[Writable]]falseのため

みたいな感じです。

速度比較

まず、次のグラフをご覧ください。これはJavaScriptのstrictモードにおいて、undefinedを比較するときに、どのようにしたら処理が速いのかをブラウザごとに比較してみた結果です。

グラフ横軸は1秒間あたりの実行回数なので、右にいくほど良い値となります。

サンプルが少ないので参考程度に見たほうがよい気がしますが、Chrome 30を除いてvoid 0を利用したものが一番速い結果となっています。

なお、Google Closureコンパイラではundefinedvoid 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 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。