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]]があるものの、外から扱えるメソッドじゃあないんだよなあ。
参考
これ見て思いつきました。