JS 中的类型判断

158 阅读3分钟

JS 中的数据类型

JS 的数据类型分为简单数据类型复杂数据类型。其中,简单数据类型包括 string number null undefined boolean symbol,这类数据占用空间固定,为了提升访问速度,将其存在中,即按值访问;复杂数据类型包括object array date等等...,这类数据的占用空间不固定,放在栈中则会影响其访问速度,所以数据的值存在中,变量中储存的是一个指针,指向中的地址。

形形色色的类型检测

鉴于 ECMAScript 是松散类型的,因此需要有一种手段来检测给定变量的数据类型。对于这个问题,JavaScript 也提供了多种方法,但遗憾的是,不同的方法得到的结果参差不齐。
下面给出四种方法以供参考

typeof 方法

typeof 是一个操作符,其后就是需要检测的操作数,它将操作数的类型以全小写字母的形式返回,包括number、boolean、symbol、string、object、undefined、function
看下面的调用示例:

typeof '' // string
typeof 1  // string
typeof false // boolean
typeof {} // object
typeof undefined // undefined
typeof null // object  检测不出是 null
typeof symbol('') //symbol
typeof function() {} // function
typeof [] // object 检测不出是数组

typeof 操作符存在的问题

  • 除了 null 以外的基本数据类型都能正确的检测出类型
  • Array,Date 等对象返回 object,不够准确
  • null 会返回 object,但 null 的类型是 Null

instanceof

instanceof 方法用于检测一个对象是否属于另一个对象的实例,简单来讲 A instanceof B ,如果 A 是 B 实例对象就返回 true,否则返回 false。 看下面的检测实例:

[] instanceof Array; // true
{} instanceof Object;// true
new Date() instanceof Date;// true
 
function Person(){};
newPerson() instanceof Person;
 
[] instanceof Object; // true
new Date() instanceof Object;// true
new Person instanceof Object;// true

从上面的代码中可以看出,instanceof 稚嫩那个用来检测两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型。
instanceof 的检测原理也很简单,就是进行原型检测,看下面演示代码:

function _instanceof(A,B) {
    const a = A.__proto__;
    return a = B.prototype;
} 

这段代码不完全,实际会便利 A 原型链上的所有层级的 __proto__ 属性,只要有相等的,就会返回 true,否则返回 false。所以这种方法检测的类型不是很准确,当你的数据是继承方式获得话,它可能被检测为多种类型,使用时要注意

constructor 方法

当一个函数被定义时,JS 引擎就会将自身的以用挂在函数的 constructor 属性上:

function a() {}
a.constructor // {constructor: function a() {}, ....}

利用这种机制,可以对数据类型做简单的检测,看下面的实例:

"".constructor = String;
Number(1).constructor = Number;
true.constructor = Boolean;
new Function().constructor = Function

从上面的代码可以看出,这种检测方式不可靠,因为 constructor 属性值时会被更改的,而这个更改是你感知不到的,如果你觉得这个描述不直观,上一段 ES5 时期的经典代码:

function A() {}

A.prototype.a = function () {}

function B() {
    A.call(this)
}

B.prototype = A.prototype

上面代码中,B 实例化对象的 constructor 将指向 A,而不是 B,这就会导致类型检测错误,但是这个更改可能是你无意识的。所以这种方法在检测类型时不可取。

toString

toString() 方法是 Object 原型方法,它会检测数据的内部属性 [[class]],这个属性的值一般为 [Object Xxx],其中 Xxx 就指的数据类型。 看下面的示例:

Object.prototype.toString.call('') ;   // [object String]
Object.prototype.toString.call(1) ;    // [object Number]
Object.prototype.toString.call(true) ; // [object Boolean]
Object.prototype.toString.call(Symbol()); //[object Symbol]
Object.prototype.toString.call(undefined) ; // [object Undefined]
Object.prototype.toString.call(null) ; // [object Null]
Object.prototype.toString.call(newFunction()) ; // [object Function]
Object.prototype.toString.call(newDate()) ; // [object Date]
Object.prototype.toString.call([]) ; // [object Array]
Object.prototype.toString.call(newRegExp()) ; // [object RegExp]
Object.prototype.toString.call(newError()) ; // [object Error]
Object.prototype.toString.call(document) ; // [object HTMLDocument]
Object.prototype.toString.call(window) ; //[object global] window 是全局对象 global 的引用

这个方法能满足你大部分的类型检测需求,但是需要事先先保存好各个数据的 [[class]] 属性的值,方便做比较和检测。