|

typeof 与 instanceof 做类型检测的异同

在 JavaScript 中进行数据类型检测常会用到 typeof 关键字和 instanceof 关键字,虽然上述两个关键字都可以进行类型校验,但实质上 instanceof 本质并不是检测类型,而是检测原型链,所以本文主要整理 typeofinstanceof 的区别,以及如何进行更好的数据类型检测。

typeof类型检测

使用 typeof 进行类型检测时,可能的返回结果有:

  • undefined
  • number
  • string
  • object
  • boolean
  • function

例如:

typeof 1            // number
typeof '1'          // string
typeof true         // boolean
typeof a            // undefined
typeof window.alert // function
typeof window       // object
typeof {}           // object
typeof []           // object
typeof null         // object

由上可以看到,在对数组进行类型检测是返回的是 object ,这并不符合预期的目标。出现以上类型检测的问题,一部分是由于历史原因,另一部分是由于类型的字节码所决定的。

在 JavaScript 中,数据类型可以大体分为 基础类型引用类型 两大类,一般我们常用到的 字符串、数字 等都属于基础类型,基础类型的变量中存放的是变量的值。而 数组、对象、函数等都是引用类型,这些类型的数据变量中存放的不再是数据的值,而是数据的指针。

关于基础类型与引用类型的理解,如果有 C/C++ 语言开发基础则非常好理解。基础类型就相当于普通变量,普通变量在内存中的字节区间可以完全存放这个变量的值,所以直接存变量值即可;引用类型相当于指针,在普通变量存不下某个值的情况下,可以通过指针变量来存放变量的指针,已达到指示目标作用。

言归正传,在 JavaScript 中使用 typeof 检测数据类型时实际上检测的是数据类型的字节码,由于 对象数组 以及 null 的末尾字节码相同,所以便得出了相同的检测结果。

instanceof 原型检测

typeof 不同,instanceof 并不检测字节码,而是进行链式检测。语法为:

A instanceof B

如果 A 是 B 的对象,则返回 true,否则返回 false。

这里的 A 和 B 不是直接进行类型检测,而是进行原型链检测,即表示:A 是否在 B 的原型链上。

由于 JavaScript 没有标注的类的概念,所以在 JavaScript 中实现继承是通过原型和原型链继承来实现的,而 作为一种自定义的类型,其实例化的对象则可以通过 instanceof 关键字来检测。

使用 instanceof 进行类型检测只会返回 true 或者 false,如:

function ClassA() {}

var objA = new ClassA();
console.log(objA instanceof ClassA);        // true
console.log(ClassA instanceof Function);    // true
console.log(Function instanceof Object);    // true
console.log(objA instanceof Object);        // true

同理:

var arr = [1, 2, 3];
console.log(arr instanceof Array);          // true
console.log(Array instanceof Object);       // true
console.log(arr instanceof Object);         // true

所以,使用 instanceof 可以检测一个对象是否在另一个对象的原型链上,或者说,对象 A 是否与 B 存在继承关系。

更好的类型检测

虽然 typeofinstanceof 各有利弊,但在开发中这两个关键字依然不能完全满足我们的需要,我们希望对一个数组进行类型检测时可以返回是数组类型,而不是返回 object 。

可以使用Object.propotype.toString.call 方法来对类型进行字符串转化,即可得到符合预期的结果。

如:

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([]);             // [object Array]
Object.prototype.toString.call({});             // [object Object]
Object.prototype.toString.call(window.alert);   // [object Function]
Object.prototype.toString.call(null);           // [object NUll]
Object.prototype.toString.call(undefined);      // [object Undefined]

使用 Object.prototype.toString.call 便可以得到更符合预期的结果。可以将该检测封装成一个函数:

function testType(data) {
    let res = Object.prototype.toString.call(data).toLowerCase();
    let type = Array.prototype.slice.call(res).slice(8, -1).join('');
    return type;
}

console.log(testType(1));               // number
console.log(testType('1'));             // string
console.log(testType(true));            // boolean
console.log(testType(alert));           // function
console.log(testType([]));              // array
console.log(testType({}));              // object
console.log(testType(null));            // null
console.log(testType(undefined));       // undefined

引申:基础类型检测

很多时候在项目的开发中,我们需要知道某个数据的类型是否为原始类型,即基础类型,包括(number,string,boolean),所以,使用以上函数,可以再额外封装出两个函数,来进行原始类型和引用类型的检测。

如下:

// 是否为基础类型
function isPrimitiveType(data) {
    let type = testType(data);
    return ['string', 'number', 'boolean'].indexOf(type) > -1 ? true : false;
}

// 是否为引用类型
function isReferenceType(data) {
    let type = testType(data);
    return ['function', 'object', 'array'].indexOf(type) > -1 ? true : false;
}

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注