数据类型检测

常见的类型检测的分为以下几种,优缺点各不相同。

typeof

typeof 对于原始类型来说,除了 null 都可以显示正确的类型

1
2
3
4
5
6
7
8
console.log(typeof 2);               // number
console.log(typeof true); // boolean
console.log(typeof 'str'); // string
console.log(typeof []); // object []数组的数据类型在 typeof 中被解释为 object
console.log(typeof function(){}); // function
console.log(typeof {}); // object
console.log(typeof undefined); // undefined
console.log(typeof null); // object null 的数据类型被 typeof 解释为 object

typeof 对于对象来说,除了函数都会显示 object,所以说 typeof 并不能准确判断变量到底是什么类型,所以想判断一个对象的正确类型,这时候可以考虑使用 instanceof

instanceof

instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype

1
2
3
4
5
6
7
8
console.log(2 instanceof Number);                    // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true
// console.log(undefined instanceof Undefined);
// console.log(null instanceof Null);

instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型;
而 typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了 function 类型以外,其他的也无法判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 我们也可以试着实现一下 instanceof
function _instanceof(left, right) {
// 由于instance要检测的是某对象,需要有一个前置判断条件
//基本数据类型直接返回false
if(typeof left !== 'object' || left === null) return false;

// 获得类型的原型
let prototype = right.prototype
// 获得对象的原型
left = left.__proto__
// 判断对象的类型是否等于类型的原型
while (true) {
if (left === null)
return false
if (prototype === left)
return true
left = left.__proto__
}
}

console.log('test', _instanceof(null, Array)) // false
console.log('test', _instanceof([], Array)) // true
console.log('test', _instanceof('', Array)) // false
console.log('test', _instanceof({}, Object)) // true

constructor

1
2
3
4
5
6
console.log((2).constructor === Number); // true
console.log((true).constructor === Boolean); // true
console.log(('str').constructor === String); // true
console.log(([]).constructor === Array); // true
console.log((function() {}).constructor === Function); // true
console.log(({}).constructor === Object); // true

如果创建一个对象,更改它的原型,constructor就会变得不可靠了

1
2
3
4
5
6
7
8
function Fn(){};

Fn.prototype=new Array();

var f=new Fn();

console.log(f.constructor===Fn); // false
console.log(f.constructor===Array); // true

Object.prototype.toString.call()

toString() 是 Object 的原型方法,调用该方法,可以统一返回格式为 “[object Xxx]” 的字符串,其中 Xxx 就是对象的类型。对于 Object 对象,直接调用 toString() 就能返回 [object Object];而对于其他对象,则需要通过 call 来调用,才能返回正确的类型信息。我们来看一下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Object.prototype.toString({})       // "[object Object]"
Object.prototype.toString.call({}) // 同上结果,加上call也ok
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('1') // "[object String]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(function(){}) // "[object Function]"
Object.prototype.toString.call(null) //"[object Null]"
Object.prototype.toString.call(undefined) //"[object Undefined]"
Object.prototype.toString.call(/123/g) //"[object RegExp]"
Object.prototype.toString.call(new Date()) //"[object Date]"
Object.prototype.toString.call([]) //"[object Array]"
Object.prototype.toString.call(document) //"[object HTMLDocument]"
Object.prototype.toString.call(window) //"[object Window]"

// 从上面这段代码可以看出,Object.prototype.toString.call() 可以很好地判断引用类型,甚至可以把 document 和 window 都区分开来。
1
2
3
4
5
6
7
8
9
10
11
function typeOf1(obj){
let type = typeof obj;
if (type !== "object") {
return type;
}
return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1'); // 注意正则中间有个空格
}

function typeOf2(){
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase()
}

总结

  • typeof
    • 直接在计算机底层基于数据类型的值(二进制)进行检测
    • typeof null为object 原因是对象存在在计算机中,都是以000开始的二进制存储,所以检测出来的结果是对象
    • typeof 普通对象/数组对象/正则对象/日期对象 都是object
    • typeof NaN === ‘number’
  • instanceof
    • 检测当前实例是否属于这个类的
    • 底层机制:只要当前类出现在实例的原型上,结果都是true
    • 不能检测基本数据类型
  • constructor
    • 支持基本类型
    • constructor可以随便改,也不准
  • Object.prototype.toString.call([val])
    • 返回当前实例所属类信息

判断 Target 的类型,单单用 typeof 并无法完全满足,这其实并不是 bug,本质原因是 JS 的万物皆对象的理论。因此要真正完美判断时,我们需要区分对待:

  • 基本类型(null): 使用 String(null)
  • 基本类型(string / number / boolean / undefined) + function: - 直接使用 typeof即可
  • 其余引用类型(Array / Date / RegExp Error): 调用toString后根据[object XXX]进行判断
------本文结束 感谢阅读------