js数据类型检测 typeof 和 instanceof 检测原理

2,045 阅读3分钟

typeof 原理

在javascript中,我们判断一个变量的类型经常用的是typeof来判断number, string, object, boolean, function, undefined, symbol(es6新增数据类型,我还没用到过) 这七种类型;在判断基本数据类型的时候,能告诉我们具体是哪种类型,但是在判断引用类型的时候,比如object,不能确定的告诉我们具体是哪种数据类型,如:

        function Person() {

	}
	var p = new Person();
	console.log(typeof p === 'object'); //true
	console.log(p instanceof Person);  //true

在用typeof判断的时候,只会告诉我们是object,而不会告诉我们具体是哪种object,有一个特例就是  typeof null === ''object'' 返回true ;所以在判断具体是哪种object类型的时候都是用instanceof 来判断,先说说typeof原理:

在 javascript 的最初版本中,使用的 32 位系统,为了性能考虑使用低位存储了变量的类型信息:

  • 000:对象
  • 1:整数
  • 010:浮点数
  • 100:字符串
  • 110:布尔

有 2 个值比较特殊:

  • undefined:用 - (−2^30)表示。
  • null:对应机器码的 NULL 指针,一般是全零。
所以,typeof 在判断 null 的时候就出现问题了,由于 null 的所有机器码均为0,因此直接被当做了对象来看待;

typeof null === 'object';
null instanceof Object === false

但是

null instanceof null // TypeError: Right-hand side of 'instanceof' is not an object

这是js历史遗留的一个bug  所以null也成为 "薛定谔的对象";

因此在用 typeof 来判断变量类型的时候,我们需要注意,最好是用 typeof 来判断基本数据类型(包括symbol),避免对 null 的判断;

instanceof 原理

MDN官方文档中是这样介绍的

instanceof运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置
// 定义构造函数
function C(){} 
function D(){} 

var o = new C();


o instanceof C; // true,因为 Object.getPrototypeOf(o) === C.prototype


o instanceof D; // false,因为 D.prototype不在o的原型链上

o instanceof Object; // true,因为Object.prototype.isPrototypeOf(o)返回true
C.prototype instanceof Object // true,同上

C.prototype = {};
var o2 = new C();

o2 instanceof C; // true

o instanceof C; // false,C.prototype指向了一个空对象,这个空对象不在o的原型链上.

D.prototype = new C(); // 继承
var o3 = new D();
o3 instanceof D; // true
o3 instanceof C; // true 因为C.prototype现在在o3的原型链上

根据instanceof的检测原理,我们可以实现一个自定义的myInstanceof

	function Person() {

	}
	var p = new Person();

	function myInstanceof(l, r) { 
	    let rValue = r.prototype; // 取右表达式的 prototype 值
	    let lValue = l.__proto__; // 取左表达式的__proto__值
	    //对待检测的对象原型进行遍历
	    while (true) {
	    	if (lValue === null) {
	            return false;	
	        }
	        if (lValue === rValue) {
	            return true;	
	        } 
	        lValue = lValue.__proto__;
	    }
	}

	console.log(myInstanceof(p,Person)); //true
        console.log(myInstanceof(p,Object)); //true        console.log(myInstanceof(p,Array)); //false 

还有一个不错的判断类型的方法,就是Object.prototype.toString,我们可以利用这个方法来对一个变量的类型来进行比较准确的判断

Object.prototype.toString.call(1) // "[object Number]"

Object.prototype.toString.call('helloworld') // "[object String]"

Object.prototype.toString.call({a:'hello world'}) // "[object Object]"

Object.prototype.toString.call([1,'a',false]) // "[object Array]"

Object.prototype.toString.call(true) // "[object Boolean]"

Object.prototype.toString.call(() => {}) // "[object Function]"

Object.prototype.toString.call(null) // "[object Null]"

Object.prototype.toString.call(undefined) // "[object Undefined]"

Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"

这里我将这个方法封装成一个函数,可参考使用:

var isType = (function () {
		var types = {};
		var t = ['Array','String','Number'];
		for(var i = 0;i<t.length;i++){
			(function (type) {
				types['is'+type] = function (obj) {
					return Object.prototype.toString.call(obj) === '[object '+type+']';
				}
			})(t[i])
		}
		return types;
	})();
	isType.isArray([]); //true