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