[ JavaScript ] - 检测数据类型

[ JavaScript ] - 检测数据类型

动态类型

各类编程语言内置的数据结构都有所不同,而ECMAScript的类型系统是松散的、动态或者是弱类型。在定义变量时,不用提前确定其具体类型,反而能在运行当中可以随意更改其数据类型:

let a = 123;
a = '123';
console.log(a); // a => '123'

在开发发过程当中,不可避免为了确定数据类型而头疼。

开发者需要一定的手段来随时确定数据具体类型。

typeOf

typeOf在一般的情况下确实能满足开发者的要求,但是它却是经常返回一些技术上是正确,让人感到不解的答案。

比如null,在ECMAScript里面定义有一个自己的基础数据类型null,包括其他的一些数组、正则、日期等,返回的都是原型链上最顶端的object,从技术上说,没有错,但不是我们想要的结果。

数据类型效果
String有效
Number有效
Symbol有效
Boolean有效
Undefined有效
Null无效
Array无效
Function有效
Date无效
regExp无效

很明显,typeOf只能检测部分数据类型,这对于开发者来说并不满意。

instanceof

根据 ECMAScript 规范,这个符号作为一个属性表示“一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例“。

注意这里的描述:instanceof检测的是原型

[] instanceof Array;// true
{} instanceof Object;// true
[] instanceof Object;// true

这里能出一点端倪,通过instanceof判断出[]不仅是Array的实例,也是Object的实例。

[]、Array、Object显然是包含某种关系,[]的原型链指向了Array.prototype,而Array.prototype.__proto__ 又指向了Object.prototype,最终 Object.prototype.__proto__ 指向了null,标志着原型链的结束。因此,[]、Array、Object 就在内部形成了一条原型链:

从原型链可以看出,[]__proto__ 直接指向Array.prototype,间接指向 Object.prototype,所以按照 instanceof 的判断规则,[] 就是Object的实例。

依次类推,类似的 new Date()new Person() 也会形成一条对应的原型链 。

因此,instanceof 只能用来判断两个对象是否属于实例关系**, 而不能判断一个对象实例具体属于哪种类型。**

instanceof不能区别undefinednull,而且对于基本类型如果不是用new声明的则也测试不出来,对于是使用new声明的类型,它还可以检测出多层继承关系。

constructor

当一个函数被定义时,JavaScript会为该函数添加 prototype 原型,并为其添加一个constructor 属性,并让其指向该函数 的引用。如下所示:

使用一个方法作为构造函数来创建对象时:

new

可以得知,函数在创建时,利用constructor引用了自身,通过构造函数来创建对象时,原型上的constructor就被遗传到了新对象上,这样做的话可让新对象具有可追溯的数据类型。

相同的,JavaScript在构建内置对象时也是如此:

new Number(1).constructor === number; // true
new Date().constructor === Date; // true
// 其他的内置对象也是如此

需要注意到,constructor不能判断undefinednull

并且使用contructor的指向是可以改变的,所以尽量的避免使用。

当然,重写对象原型时可以重新给constructor 赋值,以保证对象实例的类型不被更改。

toString

toString()Object 的原型方法,默认返回当前对象的 [[Class]] 。这是一个内部属性,其格式为 [object Xxx] ,其中 Xxx 就是对象的类型。

console.log(Object.prototype.toString.call(bool));//[object Boolean]
console.log(Object.prototype.toString.call(num));//[object Number]
console.log(Object.prototype.toString.call(str));//[object String]
console.log(Object.prototype.toString.call(und));//[object Undefined]
console.log(Object.prototype.toString.call(nul));//[object Null]
console.log(Object.prototype.toString.call(arr));//[object Array]
console.log(Object.prototype.toString.call(obj));//[object Object]
console.log(Object.prototype.toString.call(fun));//[object Function]

function User(){}
function Begonia(){}
Begonia.prototype = new User()
let func1 = new Begonia()
console.log(Object.prototype.toString.call(func1));//[object Object]

当然,该方法也不是绝对的,这里大部分数据的类型都可以检测出来,但是使用非原生构造函数构建的对象,就无法检测出具体的类型。

toString

需要注意到,对于函数而言,toString始终返回函数的代码。但是具体格式却是由各大浏览器厂商规定,所以必然包含某些差异,甚至包含注释。所以在重要功能上,尽量不要使用此方法。应只在调试中使用它。

ES2019 以前,浏览器厂商可以自由决定 Function.prototype.toString()返回什么。ES2019 要求这个方法尽可能返回函数的源代码,否则返回{ [native code] }。 ------ 摘自《JavaScript高级程序设计(第4版)》

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×