JavaScriptにはnull
とundefined
と、立場が似たものがあるわけですが、さらにこのundefined
にも二種類あるよってお話です。
「本当に無い」のと「「無い」がある」のと
値がundefined
であるときは、文字通り「未定義」である場合と、「未定義という値」である場合とがあります。
var obj = { a: undefined }; console.log(obj.x); // => undefined(定義されていないプロパティ) console.log(obj.a); // => undefined(未定義という値)
二種類のundefined
の見分け方
in
演算子で判別できます。
var obj = { a: undefined }; console.log('x' in obj); // => false(ないよ) console.log('a' in obj); // => true(あるよ)
継承したものの判別は .hasOwnProperty()
で
このときin
はprototype chainをたどります。そのオブジェクト自体で定義されていなくても、継承されていればtrue
になります。
これを見分ける場合は .hasOwnProperty()
を使います。
// 元のオブジェクト var obj1 = { prop_defined_at_obj1: undefined }; // obj1を継承したオブジェクトを作成するコンストラクター function F() {} F.prototype = obj1; // obj1を継承したオブジェクト var obj2 = new F(); obj2.prop_defined_at_obj2 = undefined; // 独自に値を追加
で、それぞれ確認してみます。
// obj2で定義されたもの console.log(obj2.prop_defined_at_obj2); // => undefined console.log('prop_defined_at_obj2' in obj2); // => true console.log(obj2.hasOwnProperty('prop_defined_at_obj2')); // => true // obj1で定義されたもの console.log(obj2.prop_defined_at_obj1); // => undefined console.log('prop_defined_at_obj1' in obj2); // => true console.log(obj2.hasOwnProperty('prop_defined_at_obj1')); // => false // どこでも定義されていないもの console.log(obj2.prop_not_defined); // => undefined console.log('prop_not_defined' in obj2); // => false console.log(obj2.hasOwnProperty('prop_not_defined')); // => false
プロパティ名 | 得られる値 | in 判定 |
.hasOwnProperty() 判定 |
---|---|---|---|
obj2.prop_defined_at_obj2 |
undefined |
true |
true |
obj2.prop_defined_at_obj1 |
undefined |
true |
false |
obj2.prop_not_defined |
undefined |
false |
false |
変数
変数も定義だけして値を代入してないときはundefined
になります。
var a; console.log(a); // => undefined
定義自体していない場合は参照エラーになります。
try { console.log(variable_not_defined_anywehere); // => ReferenceError } catch (e) { console.log(e); }
巻き上げ
ちなみに定義はスコープ内のどこでも良いです。JSエンジンがパース時に自動的に「巻き上げ」てくれます。
try { console.log(variable_defined_at_last); // => undefined console.log(variable_not_defined_anywehere); // => ReferenceError var variable_defined_at_last = 123; } catch (e) { console.log(e); }
ただし宣言は巻き上げても値の代入は巻き上げないので、先に参照した場合はundefined
を得る事になります。
つまりこれ↓
console.log(a); var a = 123;
は、これ↓
var a; console.log(a); a = 123;
と同じ。
in
と .hasOwnProperty()
の速度
in
にはprototype chainをたどるコストが、.hasOwnProperty()
には関数実行のコストがかかります。どっちが早いかな?
in
の方が早いらしい。
よくわからないこと
なんでin
は演算子なのに .hasOwnProperty()
はメソッドなのか?
メソッドの方が演算子よる便利なのは、上書きできる点。(もちろんデメリットでもあるけど。) .toString()
みたいに暗黙的に使われてたりすると便利かもしれないけど、どこかで使われる機会があるのかな。
prototype chainをたどる .hasProperty()
があってそれと対になっている、というのなら納得できるんだけど、内部的に[[HasProperty]]
があるものの、外から扱えるメソッドじゃあないんだよなあ。
参考
これ見て思いつきました。