如何检测变量的类型

1. typeof

typeof 3 // "number"
typeof "abc" // "string"
typeof {} // "object"
typeof true // "boolean"
typeof undefined // "undefined"
typeof function(){} // "function"
typeof []   // "object"
typeof null     // "object"

typeof只能用于基本数据类型检测,对于null还有Bug。不能检测引用数据类型

2. instanceof

instanceof操作符用于检查某个对象的原型链是否包含某个构造函数的prototype属性

// 比如直接原型关系
function Animal() { }
console.log((new Animal) instanceof Animal)     // true
// 原型链上的间接原型
function Cat(){}
Cat.prototype = new Animal
(new Cat) instanceof Animal         // true

instanceof也可以用来检测内置兑现,比如Array, RegExp, Object, Function:

console.log([1, 2, 3] instanceof Array) //true
console.log(/abc/ instanceof RegExp)
console.log(({}) instanceof Object)
console.log((function () { }) instanceof Function)

3. constructor

constructor属性返回一个指向创建了该对象原型的函数引用。需要注意的是,该属性的值是那个函数本身。

function Animal() { }
		var a = new Animal
		console.log(a.constructor === Animal)   // true

constructor不适合用来判断变量类型。首先因为它是一个属性,所以非常容易被伪造:

function Animal(){}
		var a = new Animal
		console.log(a.constructor === Array)
		console.log(a.constructor === Animal)    // false

另外constructor指向的是最初创建当前对象的函数,是原型链最上层的那个方法:

function Animal() { }
		function Cat() { }
		Cat.prototype = new Animal

		function BadCat() { }
		BadCat.prototype = new Cat

		console.log((new BadCat).constructor === Animal)   // true
		console.log(Animal.constructor === Function)       // true

在访问基本数据类型的属性时,JavaScript会自动调用其构造函数来生成一个对象。例如:

(3).constructor === Number // true
true.constructor === Boolean // true
'abc'.constructor === String // true
// 相当于
(new Number(3)).constructor === Number
(new Boolean(true)).constructor === Boolean
(new String('abc')).constructor === String

4. toString

toString方法是最为可靠的类型检测手段,它会将当前对象转换为字符串并输出。 toString属性定义在Object.prototype上,因而所有对象都拥有toString方法。 但Array, Date等对象会重写从Object.prototype继承来的toString, 所以最好用Object.prototype.toString来检测类型。

toString = Object.prototype.toString;
		console.log(toString.call(new Date))    // [object Date]
		console.log(toString.call(new String));  // [object String]
		console.log(toString.call(Math));        // [object Math]
		console.log(toString.call(3));           // [object Number]
		console.log(toString.call([]));          // [object Array]
		console.log(toString.call({}));          // [object Object]

		// Since JavaScript 1.8.5
		console.log(toString.call(undefined));   // [object Undefined]
		console.log(toString.call(null));        // [object Null]

toString也不是完美的,它无法检测用户自定义类型。 因为Object.prototype是不知道用户会创造什么类型的, 它只能检测ECMA标准中的那些内置类型。

toString.call(new Animal)   // [object Object]

因为返回值是字符串,也避免了跨窗口问题。

跨窗口问题

我们知道Javascript是运行在宿主环境下的,而每个宿主环境会提供一套ECMA标准的内置对象,以及宿主对象(如window,document),一个新的窗口即是一个新的宿主环境。 不同窗口下的内置对象是不同的实例,拥有不同的内存地址。

而instanceof和constructor都是通过比较两个Function是否相等来进行类型判断的。 此时显然会出问题,例如:

var iframe = document.createElement('iframe');
var iWindow = iframe.contentWindow;
document.body.appendChild(iframe);

iWindow.Array === Array         // false
// 相当于
iWindow.Array === window.Array  // false

因此iWindow中的数组arr原型链上是没有window.Array的。请看:

iWindow.document.write('<script> var arr = [1, 2]</script>');
iWindow.arr instanceof Array            // false
iWindow.arr instanceof iWindow.Array    // true

总结

  • typeof只能检测基本数据类型,对于null还有Bug;

  • instanceof适用于检测对象,它是基于原型链运作的;

  • constructor指向的是最初创建者,而且容易伪造,不适合做类型判断;

  • toString适用于ECMA内置JavaScript类型(包括基本数据类型和内置对象)的类型判断;


如何检测变量的类型
https://blog.fullsize.cn/2021/04/14/notion/ru-he-jian-ce-bian-liang-de-lei-xing/
作者
fullsize
发布于
2021年4月14日
许可协议