[追記]id:zorioさんのブックマークコメントの指摘により、抜けていた「.apply」を挿入しました。[/追記]
JavaScriptで、大域変数(大域オブジェクトのプロパティ)fooが存在しないときに何かをしたいとしましょう。例えば、こんな書き方をしますよね。
if (!foo) {
// 何か
}
でも、式 !foo
は、fooがnullでもfalseでも0でもtrueに評価されます。
としたほうがよさそうです。が、
if (foo == undefined) {
// 何か
}
null == undefined
もtrueとなるのであまり改善されてません。イコールが3つなら、厳密に「fooが未定義値であるとき」を表現します。ここで、undefinedはリテラルではなくて、仕様として事前に定義されている大域変数です。undefinedは変数ですから値を書き換えることができます(例えば、
if (foo === undefined) {
// 何か
}
undefined = "hello";
)。万が一の事態を用心するなら、undefinedは使わないほうがいいでしょう。
if (foo === (void 0)) {
// 何か
}
ところで、僕はJavaScript対話系Rhinoを厳密モード(strict mode)で使っているのですが、if (foo === (void 0)) {...}
のように書くと、
と叱られます。大域変数への参照をいきなり名前で書くのではなくて、window.fooのようなドット形式にすれば大丈夫です。たまたま局所変数fooがあって大域変数fooが隠れてしまうような事故も防げます。
Attempt to assign non-existing name "foo" in the strict mode.
It could indicate a missing variable statement.
if (window.foo === (void 0)) {
// 何か
}
あるいは、
if (typeof window.foo == 'undefined') {
// 何か
}
あれっ? 待てよ。Rhinoではそもそもwindowってのが定義されてないからやっぱりエラーでした。ハニャニャニャ。大域オブジェクトを名前によらずに確実に参照するには、(function() {return this;}).apply(null, [])
って式が使えます*1。
なげー、みにくい。変数を使いましょう。
if (typeof ((function() {return this;}).apply(null, [])).foo == 'undefined') {
// 何か
}
var g = (function() {return this;}).apply(null, []);
if (typeof g.foo == 'undefined') {
// 何か
}
これでよいかな? いやっ、実はこれでは存在判定になってません。プロパティg.fooが存在してかつ値が未定義値のときもtypeof g.foo == 'undefined'
は真になりますからね。in演算子を使うとよさそうです。
var g = (function() {return this;}).apply(null, []);
if (!("foo" in g)) {
// 何か
}
実際上は、値が未定義値のときは“存在しないも同様”とみなして、たいてい大丈夫ですけどね。
*1:この方法は、以前id:nak2kさんに教えていただきました。http://d.hatena.ne.jp/m-hiyama/20050927/c 参照