JavaScript类型检测小结(转)

老话题了,各种方案和相关讨论都不少,简单总结下:

悠久的typeof

JavaScript里只有五种基本数据类型:number, string, boolean, null, undefined. 其它的都是复合数据类型object.

值和数据类型是两码事。比如:null是null类型的唯一值,undefined是undefined类型的唯一值,就如true和false是boolean类型的唯两值一样。

可以用typeof检测出以下6种数据类型:number, string, boolean, undefined, object, function.

注意:typeof null == “object”. null类型的唯一值null的类型是object类型。(很拗口,但事实就是这样)

因此,对于五种基本数据类型来说,用下面的代码就可以检测出来:

// 获取变量o的数据类型 function type(o) { return (o === null) ? 'null' : typeof(o); }

instanceof的作用

typeof只能检测基本数据类型,对于复合数据类型,除了function,都通通返回'object'.

instanceof可以检测某个对象是不是另一个对象的实例,注意instanceof的右操作数必须为对象:

alert(1 instanceof Number); // false alert({} instanceof Object); // true

instanceof还可以检测父类型:

function Animal() {}; function Pig() {}; Pig.prototype = new Animal(); alert(new Pig() instanceof Animal); // true

可以看出,instanceof不适合用来检测一个对象本身的类型。

救命稻草:constructor

JavaScript里的所有对象都拥有一个constructor属性,但JavaScript里并非一切都是对象:

alert(1.constructor); // 报错 var o = 1; alert(o.constructor); // Number o = null; // or undefined alert(o.constructor); // 报错 alert({}.constructor); // Object alert(true.constructor); // Boolean

可以看出,null和undefined没有constructor,number和string数据类型的字面量,也没有 constructor,但number和string数据类型的变量有constructor(在寻找constructor属性时,会自动转换成 Number或String对象)。

下面是Johg Resig《Pro JavaScript Techniques》书中的一张表:
constructor

这样,对于复合数据类型,我们可以采用下面的方法检测:

... isArray: function(arr) { return !!arr && arr.constructor == Array; } ...

jQuery 1.2中采用的就是上面的代码。

一切看起来很完美,然而不安分的iframe总喜欢来捣点鬼:

// 请在非ie浏览器中运行 var iframe = document.createElement('iframe'); document.body.appendChild(iframe); var xArray = window.frames[window.frames.length-1].Array; var arr = new xArray(1, 2, 3); // [1,2,3] alert(arr.constructor === Array); // false

原因很简单:不同frame中的Array拥有不同的constructor.

鸭子也上场:Duck Typing

在犀牛书里,提到一句老话:“如果走路像鸭子,叫声也像鸭子,那它就是一个鸭子!” 换言之,对于Array来说,如果一个对象有splice和join属性,那它就是Array. 这就是Duck Typing:

function isArray(o) { return o != null && typeof o === ‘object’ && 'splice' in o && 'join' in o; }

上面是Prototype 1.6中的代码。

显然,鸭子检测很容易误把自造的天鹅也当成鸭:

alert(isArray({'splice': '', 'join': ''})); // true

鸭子检测的一个用途是,可以用来检测类似对象,比如类数组:

function isArrayLike(x) { if (x instanceof Array) return true; // Real arrays are array-like if (!('length' in x)) return false; // Arrays must have a length property if (typeof x.length != 'number') return false; // Length must be a number if (x.length < 0) return false; // and nonnegative if (x.length > 0) { // If the array is nonempty, it must at a minimum // have a property defined whose name is the number length-1 if (!((x.length - 1) in x)) return false; } return true; }

上面的代码摘自犀牛书。

 

回归简单:Object.toString

这个方法并不新奇,在犀牛书的 9.7 Determining Object Type 一节中,有详细的讨论,但一直没有引起注意(犀牛书太厚,仔细阅读过的人,在世界范围内看来都是凤毛麟角的)。直到老道(Douglas Crockford)的火星文出现:The Miller Device(号召大家都向老道学习,多挖掘有价值的火星知识):

Object.prototype.toString.apply(value) === '[object Array]'

ECMA-262中的解释:

Object.prototype.toString( )
When the toString method is called, the following steps are taken:
1. Get the [[Class]] property of this object.
2. Compute a string value by concatenating the three strings “[object “, Result (1), and “]”.
3. Return Result (2)

这样,就有了:

function isArray(o) { return Object.prototype.toString.call(o) === '[object Array]'; }

因为是字符串比较,也就解决了跨iframe的问题。

结合typeof和toString方法,可以比较完美的实现对JavaScript基本类型和内置对象的探测了:

var _toS = Object.prototype.toString, _types = { 'undefined' : 'undefined', 'number' : 'number', 'boolean' : 'boolean', 'string' : 'string', '[object Function]' : 'function', '[object RegExp]' : 'regexp', '[object Array]' : 'array', '[object Date]' : 'date', '[object Error]' : 'error' }; function type(o) { return _types[typeof o] || _types[_toS.call(o)] || (o ? 'object' : 'null'); }

详细测试页面请参考这里:typeof.js

自定义对象惹的祸

来看下面的代码:

function Animal() {} function SubArray() {} SubArray.prototype = []; var toString = Object.prototype.toString; alert(toString(new Animal())); alert(toString(new SubArray())); // firefox: [object Window] // ie: [object Object] // chrome: [object global] alert(new SubArray() instanceof Array); // true alert(new Animal() instanceof Animal); // true

可以看出,Object.toString方法,对于非内置对象来说,在各个浏览器下返回的值各异。

因此,如果要检测非内置对象,还是得用constructor和instaceof. 或者像Mootools一样,通过框架的机制来判断:

Mootools has an interesting approach with it’s $type function. Mootools wraps all native objects via the “Native” constructor in order to easily implement new methods for them. The Native constructor also creates a new property, called $family, which is the value returned by the $type() utility function.

 

参考资料

  1. 文中提到的犀牛书指的是:OReilly.JavaScript.The.Definitive.Guide.5th.Edition.Aug.2006
  2. How to write a robust ‘isArray’(很详细的介绍,一步一步,很有条理)
  3. The Miller Device(老道的文章总是言简意赅,一针见血……)
  4. getClass.js(toString方法检测Class的一个实现)
  5. typeof增加版(很不错的实现方案)
  6. toString in MDC(Mozilla是与时俱进的官方)
posted @ 2010-06-02 10:58  ForFreeDom  阅读(254)  评论(0编辑  收藏  举报