まてよ、ということは…

underscore.js の _.isRegExp について - ”><xmp>TokuLog 改メ tokuhirom’s blog
multiple frames のときにハマるということらしい。
Perfection kills ≫ `instanceof` considered harmful (or how to write a robust `isArray`)
This means that creating isArray function could not be simpler than:

これを応用すればまっとうなtypeofを作れるってこと?

JavaScriptのtypeofは役立たず

よく知られているように、JavaScriptのtypeofは使えない子です。JavaScript: The Good Partsでも「ひどいパーツ」と名指しです。

p(typeof(undefined));
p(typeof(null));
p(typeof(false));
p(typeof(0));
p(typeof(''));
p(typeof([]));
p(typeof({}));
p(typeof(function(){}));
p(typeof(new Date));
p(typeof(/[\S\s]/g));
p(typeof(document));
p(typeof(document.createElement('div')));
function YourOwnObject(a){
    this.a = a;
};
var foo = new YourOwnObject;
p(typeof(foo));

typeof(null)objectだったり、ArrayとObjectの区別がつかなかったり。わけがわからないよ。

Object.prototype.toString.call()があるじゃない

ところがObject.prototype.toString.call()に食わせると、なんということでしょう!

var toString = Object.prototype.toString;
p(toString.call(undefined));
p(toString.call(null));
p(toString.call(false));
p(toString.call(0));
p(toString.call(''));
p(toString.call([]));
p(toString.call({}));
p(toString.call(function(){}));
p(toString.call(new Date));
p(toString.call(/[\S\s]/g));
p(toString.call(document));
p(toString.call(document.createElement('div')));
function YourOwnObject(a){
    this.a = a;
};
var foo = new YourOwnObject;
p(toString.call(foo));
p(foo.constructor === Object);
p({}.constructor === Object);

ほとんどのbuilt-in objectを区別できるではありませんか。IEだとundefinednullやDOMなど、constructorプロパティが不在なオブジェクトがみんな[object Object]になってしまうものの、IE以外だとDOMまで嗅ぎ分けてくれます。

あとOperaだと、undefinednull[object Window]になっちゃったり、Safari for iOSだと[object DOMWindow]になっちゃったり、Androidだと[object global]になっちゃったりしますが、これも何とか出来るでしょう。

built-inでないobjectもまた[object Object]と判定されてしまいますが、これはconstructorがObjectか否かをチェックすれば判別できます。

これで割とまともなtypeOf()が書ける!

というわけで、まとめると以下のとおりになります。

var toString = Object.prototype.toString;
var NU = {
    '[object Object]':'IE',
    '[object Window]':'Opera',
    '[object DOMWindow]':'Safari iOS',
    '[object global]':'Android'
};

typeOf = function(that){
    var t = toString.call(that);
    if (!that){
        if (t in NU) {
            var u = that === null ? 'Null' : 'Undefined';
            return '[object ' + u + ']';
        }
        else return t;
    }
    if (t === '[object Object]') {
        if (that.constructor !== Object) return null
        else return t;
    }
    else return t;
};

[object Whatever]からWhateverだけ取り出す作業は割愛しています。

p(typeOf(undefined));
p(typeOf(null));
p(typeOf(false));
p(typeOf(0));
p(typeOf(''));
p(typeOf([]));
p(typeOf({}));
p(typeOf(function(){}));
p(typeOf(new Date));
p(typeOf(/[\S\s]/g));
p(typeOf(document));
p(typeOf(document.createElement('div')));

function YourOwnObject(a){
    this.a = a;
};
var foo = new YourOwnObject;
p(typeOf(foo));

Enjoy!

Dan the JavaScripter