JavaScript欲速则不达——类型检测

想要避免抛出 TpyeError 错误,我们必须得在使用前去检查该类型是否是想要传入的类型。

typeof不能检测null或undefined和引用类型数据,而使用instanceof虽然可以检测引用类型的数据,却不能检测非函数的数据。所以我们有必要有这样一个方法可以帮助我们原理这些坑。

基本方法

typeof 运算符返回这个运算对象的类型的来表明。但是它返回的类型非常有限,如下:

console.log(typeof '') //string
console.log(typeof 1) //number
console.log(typeof true) // boolean
console.log(typeof undefined) // undefined
console.log(typeof {}) // object
console.log(typeof []) // object
console.log(typeof null) // object
console.log(typeof document.createElement('div')) // object
console.log(typeof /abcd/) // object
	

我们知道,所有引用类型都是默认继承了 Object,而这个继承是通过原型链实现的。所以函数的原型对象都会包含一个指向Object构造函数的原型对象的指针,也即指向Object.prototype的指针[[prototype]]。

instanceof 运算符和 constructor 属性 可以用来检测引用数据的类型,如下:

console.log( '' instanceof String ) // false
console.log( /[String]/.test( ''.constructor ) ) // true

var num = 1;
console.log( num instanceof Number ) // false
console.log( /[Number]/.test( num.constructor ) ) // true

var is = true;
console.log( is instanceof Number ) // false
console.log( /[Boolean]/.test( is.constructor ) ) // true

console.log( [] instanceof Array ) // true
console.log( /[Array]/.test( [].constructor ) ) // true

console.log( {} instanceof Object ) // true
console.log( /[Object]/.test( {}.constructor ) ) // true

var Foo = function () {};
console.log( new Foo() instanceof Foo ) // true
console.log( /[Function]/.test( new Foo().constructor ) ) // true



var x; // 并不是所有数据类型都具有构造函数 ,也不是所有数据类型实例方法,譬如undefined null
console.log( x.constructor ) // 根本不会有 undefined 这名字的构造函数
console.log( x instanceof undefined )  // 也不会有 undefined 这样名字的实例对象
var y = null;
console.log( y instanceof Object ) // instanceof 只能检测非原始类型数据,虽然typeof null 是 object
console.log( y.constructor ) //报错
	
  • 不能使用instanceof检测,因为 instanceof 只能用来检测引用类型数据;

    instanceof所做的事情好比如下代码:

    function myInstanceof(value, Type) { 
        return Type.prototype.isPrototypeOf(value); 
    }
    			

    先取出类型对象的prototype成员,然后检测传入的对象是否是另一个对象的原型。

    而null 和 undefined 不可能具有构造函数,使用正则去匹配constructor也是不可能的。

  • 在两个框架页面中创建两个数组,其中一个框架页面中的数组不是另一个框架页面的Array()构造函数的实例,instanceof运算符结果是false。原因:每个窗口和框架子页面都具有单独的执行上下文,每个上下文都包含单独的全局变量和一组构造函数。在两个不同的框架页面中创建的两个数组是继承了两个相同但相互独立的原型对象。

prototype.toString

除了 undefined 和 null 其他数据都继承Object原型对象 toString()、valueOf()、isPrototypeOf()以及hasOwnProperty方法已经constructor属性了。并且在 15.2.4.2 这个版本的 ECMA-262 规范中,toString方法是这样定义的:

  • 如果参数是未定义的值,则返回”[object Undefined]”.
  • 如果参数为null,则返回”[object Null]”.
  • 如果适用ToObject函数传递参数,则返回对象.
  • 如果参数为类,则返回包含对象的类.(Let class be the value of the [[Class]] internal property of O.)
  • 返回一个由”[对象”, 类, 和”]”拼接而成的字符串.

因此,这个方法永远会返回一个该类型的字符串,

var type = function (o) {
    var s = Object.prototype.toString.call(o);
    return s.match(/\[object (.*?)\]/)[1].toLowerCase();
}
type({}); // "object"
type([]); // "array"
type(5); // "number"
type(null); // "null"
type(); // "undefined"
type(/abcd/); // "regex"
type(new Date()); // "date"
	

但是有些值还是会出乎我们的意料

type(NaN); // "number" 特殊的数字了类型
type(document.body); // "htmlbodyelement"
	

所以我们最终得到如下最终解决方案:

var what_type = function (o) {

    // 处理 null 在老版本IE
    if (o === null) {
        return 'null';
    }
    // 处理 DOM elements
    if (o && (o.nodeType === 1 || o.nodeType === 9)) {
        return 'element';
    }
    var s = Object.prototype.toString.call(o);
    var type = s.match(/\[object (.*?)\]/)[1].toLowerCase();
    // 处理 NaN and Infinity
    if (type === 'number') {
        if (isNaN(o)) {
            return 'nan';
        }
        if (!isFinite(o)) {
            return 'infinity';
        }
    }
    return type;
}	
	

本文源链接:http://www.html5jscss.com/javascript-type.html

转载请注明《JavaScript欲速则不达——类型检测》| html5jscss

评论关闭了.