typeof演算子はin演算子の利用可能判定に使えるか


## 導入

[JavaScript Garden]:http://bonsaiden.github.io/JavaScript-Garden/ja/#types.typeof

`undefined`の判定にしか使えないと言われるtypeof演算子が、もしかしたら変数にin演算子が使えるかを判定するのにも使えるのでは?
そう思い、この記事を投稿します。

## [追記]結局使えなかった

@think49さんの[コメント](#comment-12efe90cffea947ee610)により、結局**typeof演算子はin演算子の利用可能判定に使えませんでした**。
正確に言えば、`undefined`の判定として使ってはいますが……私の意図した「`undefined`の判定以外の用途」としては使っていません。

[詳細](#%E8%BF%BD%E8%A8%98)

## 前提事項

この記事は、クライアントサイドのJavaScript(Webブラウザで動作するJavaScript)を前提としています。
サーバサイドのJavaScriptは経験がないので、そこでの常識とは食い違った内容になっているかもしれません。

## typeof演算子の悪評

JavaScriptの中～上級者であれば、typeof演算子の利用は避けるべきということはご存知だと思います。
もし知らない方は、下記ページをご覧ください。

[JavaScript Garden]

> `typeof`演算子(`instanceof`も同様です)は恐らくJavaScriptの最大の設計ミスです。**完全に壊れている**存在に近いものです。
>
> `instanceof`はまだ限られた用途で使用できますが、`typeof`は本当に使用できる実用的なケースはオブジェクトの型を調べるという起こら**ない**ケース一つしかありません。
>
> (中略)
>
> 変数が定義されているかチェックしない限りは、`typeof`は**どんな事をしても**避けるべきです。

厳密な話をすれば、typeof演算子はプリミティブ値としてのデータ型を正しく返しているわけで、[JavaScript Garden]にあるように設計ミスでもなければ壊れてもいません。

[typeof演算子から学ぶJavaScriptのデータ型の概念と関係する考察のまとめ - 三等兵](http://d.hatena.ne.jp/sandai/20110308/p1)

しかし普通の用途では、JavaScriptのプリミティブ値のデータ型は不要です。
`new Date()`と`{}`と`/abc/g`が同じObject型で、`"foo"`と`new String("foo")`が違う型では、(たとえそれが正しくとも)判定として役に立ちません。
([サーバサイドは違うらしいですが…](#comment-ac43d780aa96db43116d))

このために、typeof演算子の利用は避けるべきという事になっています。

## typeof演算子が役に立つ唯一の機会

[JavaScript Garden]にもありますが、typeof演算子が役に立つのは変数が定義されているか確認する場合、つまり`undefined`かどうかを判定する場合です。

```js
if (typeof foo !== "undefined") {
    // code ...
}
```

上記の場合、変数`foo`が定義されているかを判定しています。

グローバル変数`undefined`はECMAScript 5になるまで上書き可能な変数でしたし、ECMAScript 5以降でも、関数内のローカル変数では`undefined`を上書き可能なため、グローバル変数`undefined`との比較による判定では無効になる場合があります。
このため、`undefined`との比較による検証はするべきではないでしょう。

また、typeof演算子は言語構造なので、指定した変数が未定義でもエラーが出ないという強みがあります。

## in演算子の利便性と問題

in演算子は、オブジェクトにプロパティがあるかどうかをプロパティ名で判定する演算子です。

```js
if ("geolocation" in navigator) {
    // code ...
}
```

このin演算子をオブジェクト以外に使うと…

```js
var foo = "foo";
if ("length" in foo) {
    // code ...
}
// TypeError: Cannot use 'in' operator to search for 'length' in foo
```

しかし、同じ文字列でも…

```js
var foo = new String("foo");
if ("length" in foo) {
    // code ...
}
```

## 本題

in演算子が利用できるのはオブジェクトです。プリミティブ値のObject型です。
~~よって、typeof演算子で以下のように判定することでin演算子が利用できる変数なのかを判定できます。~~

```js
if (foo !== null && typeof foo === "object") {
    if ("length" in foo) {
        // code ...
    }
}
```

注意点として、`null`もObject型として判定されるため、`null`を完全一致で条件から除いています。
~~`null`をObject型と判定するのは、本当の設計ミスじゃないだろうか…~~

### 追記

@think49さんの[コメント](#comment-12efe90cffea947ee610)曰く、私のコードでは[Object (host and does not implement [[Call]])](http://es5.github.io/#x11.4.3)や`function(){}`がプリミティブ値のObject型として判定されないとのこと。

確かに

```js
var foo = function () { };

console.log(!!(foo !== null && typeof foo === "object"));
// false
console.log("length" in foo);
// true
```

`function(){}`はin演算子使えますが、`false`と判定されてしまっていますね。
正しい方法は以下になります。

```js
if (foo !== null && typeof foo !== "undefined" && Object(foo) === foo) {
    if ("length" in foo) {
        // code ...
    }
}
```

私も自分のコードを修正してきます…

## 反省

眠気の影響で[in演算子の利便性と問題](#in%E6%BC%94%E7%AE%97%E5%AD%90%E3%81%AE%E5%88%A9%E4%BE%BF%E6%80%A7%E3%81%A8%E5%95%8F%E9%A1%8C)から適当に書いてしまいました。
気が向いたら修正します。

編集リクエストも受け付けていますので、もっと最適なサンプルコードがある場合や誤った説明がある場合など、どんどん編集してください。