Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
CodeRookie262 opened this issue Feb 1, 2021 · 1 comment

Comments

@CodeRookie262
Copy link
Owner

No description provided.

@CodeRookie262
Copy link
Owner Author

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

typeof

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

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

constructorinstanceof差不多,不过它可以检测基础类型,例如(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
Projects
None yet
Development

No branches or pull requests

1 participant