Skip to content

说说几种判断数据类型的方法,并说说其中各自的优缺点。 #34

Open

Description

@CodeRookie262
No description provided.

Activity

  1. CodeRookie262 commented on Feb 1, 2021

    @CodeRookie262
    OwnerAuthor

    常用判断类型的方法主要有typeof,instanceof,constructor以及Object.prototype.toString等等。

    typeof

    typeof 的检测机制是根据底层的二进制数据进行判断的,可以检测出的类型分别由number,boolean,string,undefined,function,object,symbol和bigint。

    关于 typeof 的检测有一个历史遗留的bug,那就是检测null的结果和检测对象的结果都是object,这个是因为typeof将二进制中前三位如果为 0 的判断为 object 类型,而 null 是空指针,都是 0,所以被检测为 object。typeof null 透析

    instanceof

    检测某个实例是否隶属于该类,使用方法 ++实例 instanceof 类++。
    先说说它的不足点,其实它并不能准确的用来检测类型,只不过可以用来弥补 typeof 的一些不足点。

    不足点:
    1. 不能检测基础类型,也就是所谓的字面量(如 2333,"CodeRookie262",true,Symbol()以及122n这些基础类型)
    2. 原型链可以被随意更改替换,可能会导致结果不准确;
    3. 基于类原型上的[Symbol.hasInstance](instance) 进行判断,这个方法可以随意更改,可能会导致结果不准确;
    4. 基于原型链比较,往往可以判断一个或者多个类,例如实例的直属类,还有Object;

    底层原理:
    优先查找类原型上是否存在[Symbol.hasInstance]方法,有的话则直接返回函数调用的结果,如果不存在此方法则从__proto__原型链上开始查找,直到找到类的原型constructor.prototype。

    手动实现一个 instance_of 方法来检测数据类型吧~

          var instacne_of = function instance_of(instance, constructor) {
            var instance_type = typeof instance,
              constructor_type = typeof constructor;
    
            // 判断构造函数是否是一个函数
            if (!/^function$/i.test(constructor_type) && !constructor.prototype) {
              throw new TypeError('constructor 必须为一个具有 prototype 的函数');
            }
            // 不处理原始值
            if (!/^object|function$/i.test(instance_type)) return false;
            // 判断 null
            if (instance === null) return false;
    
            // 判断是否具有 [Symbol.hasInstance] 方法
            if (typeof constructor.prototype[Symbol.hasInstance] === 'function') {
              return constructor.prototype[Symbol.hasInstance](instance);
            }
    
            // 如果没有则基于原型链查找
            // 获取当前实例的原型
            let prototype = Object.getPrototypeOf(instance);
            while (prototype) {
              if (prototype === constructor.prototype) return true;
              // 更新 prototype,知道找到 constructor 的原型或者 null 为止
              prototype = Object.getPrototypeOf(prototype);
            }
            return false;
          };
    constructor

    constructor 和 instanceof差不多,不过它可以检测基础类型,例如(1).constructor === Number,同时他只能检测它的直属类进行判断,不会和 instanceof 对原型链进行遍历向上查找原型。

    Object.prototype.toString

    Object.prototype.toString 可以说是最接近完美的方案了,可以检测很多类型,包括内置类(Number,String,Date,RegExp...);

    其中一些类自身的都具有toString方法,不过它们的这个方法并不能用来检测类型,而是将数据转换为字符串,例如Array.prototype.toString,只有 Object.prototype.toString 可以用来检测数据的类型,使用它的时候必须借用上下文重定向(说人话就是需要改变 this),利用鸭子模型进行调用从而得到对应的数据类型;

    可以看看著名的 JQuery 的源码中有一个数据类型检测就是借用它来封装的。

    var class2type = {};
    
    var toString = class2type.toString;
    
    var hasOwn = class2type.hasOwnProperty;
    填充 class2type 表
    jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
    function( i, name ) {
    	class2type[ "[object " + name + "]" ] = name.toLowerCase();
    } );
    添加数据类型检测拓展方法
    jQuery.extend({
        // 判断是否是数组
        isArray: Array.isArray,
        // 判断是否是 window 对象
    	isWindow: function( obj ) {
    		return obj != null && obj === obj.window;
    	},
        // 判断是否是数字
    	isNumeric: function( obj ) {
    		var realStringObj = obj && obj.toString();
    		return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0;
    	},
        // 判断是否是纯对象
    	isPlainObject: function( obj ) {
    		var key;
    		if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
    			return false;
    		}
    
    		// 如果构造函数中没有这些属性就返回 false
    		if ( obj.constructor &&
    				!hasOwn.call( obj, "constructor" ) &&
    				!hasOwn.call( obj.constructor.prototype || {}, "isPrototypeOf" ) ) {
    			return false;
    		}
    
    		//首先枚举自己的属性,以便加快速度,
            //如果最后一个是拥有的,那么所有属性都是拥有的
    		for ( key in obj ) {}
    
    		return key === undefined || hasOwn.call( obj, key );
    	},
        // 判断是否是空对象
    	isEmptyObject: function( obj ) {
    		var name;
    		for ( name in obj ) {
    			return false;
    		}
    		return true;
    	},
        // 判断类型
    	type: function( obj ) {
    		if ( obj == null ) {
    			return obj + "";
    		}
    
    		return typeof obj === "object" || typeof obj === "function" ?
    			class2type[ toString.call( obj ) ] || "object" :
    			typeof obj;
    	},
    })
    判断是否是类数组
    function isArrayLike( obj ) {
    	var length = !!obj && "length" in obj && obj.length,
    		type = jQuery.type( obj );
    
    	if ( type === "function" || jQuery.isWindow( obj ) ) {
    		return false;
    	}
    
    	return type === "array" || length === 0 ||
    		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions