关注『前端开发博客』公众号,回复 加群,加入高薪前端群
JS中判断数据类型的方式有不少javascript
JS数据类型分为基本类型和引用类型。
基本类型:html
引用类型前端
函数是一种特殊的对象,便可调用的对象。java
typeof
操做符能够区分基本类型,函数和对象。git
console.log(typeof null) // object console.log(typeof undefined) // undefined console.log(typeof 1) // number console.log(typeof 1.2) // number console.log(typeof "hello") // string console.log(typeof true) // boolean console.log(typeof Symbol()) // symbol console.log(typeof (() => {})) // function console.log(typeof {}) // object console.log(typeof []) // object console.log(typeof /abc/) // object console.log(typeof new Date()) // object
typeof
有个明显的bug就是typeof null
为object
;typeof
没法区分各类内置的对象,如Array
, Date
等。JS是动态类型的变量,每一个变量在存储时除了存储变量值外,还须要存储变量的类型。JS里使用32位(bit)存储变量信息。低位的1~3个bit存储变量类型信息,叫作类型标签(type tag)github
.... XXXX X000 // object .... XXXX XXX1 // int .... XXXX X010 // double .... XXXX X100 // string .... XXXX X110 // boolean
int
类型的type tag
使用1个bit,而且取值为1,其余都是3个bit, 而且低位为0。这样能够经过type tag
低位取值判断是否为int
数据;int
,还剩下2个bit,至关于使用2个bit区分这四个类型:object
, double
, string
, boolean
;null
,undefined
和Function
并无分配type tag
。Function
函数并无单独的type tag
,由于函数也是对象。typeof
内部判断若是一个对象实现了[[call]]
内部方法则认为是函数。segmentfault
undefined
undefined
变量存储的是个特殊值JSVAL_VOID
(0-2^30),typeof
内部判断若是一个变量存储的是这个特殊值,则认为是undefined
。api
#define JSVAL_VOID INT_TO_JSVAL(0 - JSVAL_INT_POW2(30))
null
null
变量存储的也是个特殊值JSVAL_NULL
,而且恰巧取值是空指针机器码(0),正好低位bit的值跟对象的type tag
是同样的,这也致使著名的bug:数组
typeof null // object
很不幸,这个bug也不修复了,由于初版JS就存在这个bug了。祖传代码,不敢修改啊。浏览器
有不少方法能够判断一个变量是一个非null
的对象,以前遇到一个比较经典的写法:
// 利用Object函数的装箱功能 function isObject(obj) { return Object(obj) === obj; } isObject({}) // true isObject(null) // false
Object.prototype.toString
通常使用Object.prototype.toString
区分各类内置对象。
console.log(Object.prototype.toString.call(1)); // [object Number],隐式类型转换 console.log(Object.prototype.toString.call('')); // [object String],隐式类型转换 console.log(Object.prototype.toString.call(null)); // [object Null],特殊处理 console.log(Object.prototype.toString.call(undefined)); // [object Undefined],特殊处理 console.log(Object.prototype.toString.call(true)); // [object Boolean],隐式类型转换 console.log(Object.prototype.toString.call( {})); // [object Object] console.log(Object.prototype.toString.call([])); // [object Array] console.log(Object.prototype.toString.call(function(){})); // [object Function]
Object.prototype.toString
不能区分基本类型的,只是用于区分各类对象;
null
和undefined
不存在对应的引用类型,内部特殊处理了;[[Class]]
每一个对象都有个内部属性[[Class]]
,内置对象的[[Class]]
的值都是不一样的("Arguments", "Array", "Boolean", "Date", "Error", "Function", "JSON", "Math", "Number", "Object", "RegExp", "String"),而且目前[[Class]]
属性值只能经过Object.prototype.toString
访问。
Symbol.toStringTag
属性其实Object.prototype.toString
内部先访问对象的Symbol.toStringTag
属性值拼接返回值的。
var a = "hello" console.log(Object.prototype.toString.call(a)); // "[object String]" // 修改Symbol.toStringTag值 Object.defineProperty(String.prototype, Symbol.toStringTag, { get() { return 'MyString' } }) console.log(Object.prototype.toString.call(a)); // "[object MyString]"
若是哪一个货偷偷修改了内置对象的Symbol.toStringTag
属性值,那Object.prototype.toString
也就不能正常工做了。
Object.prototype.toString
内部逻辑综上能够总结Object.prototype.toString
的内部逻辑:
undefined
, 则返回"\[object Undefined\]";null
, 则返回"\[object Null\]";获取对象的Symbol.toStringTag
属性值subType
subType
是个字符串,则返回[object subType]
[[Class]]
属性值type
,并返回[object type]
object instanceof constructorFunc
instanceof
操做符判断构造函数constructorFunc
的prototype
属性是否在对象object
的原型链上。
Object.create({}) instanceof Object // true Object.create(null) instanceof Object // false Function instanceof Object // true Function instanceof Function // true Object instanceof Object // true
instanceof
操做符不会对变量object
进行隐式类型转换"" instanceof String; // false,基本类型不会转成对象 new String('') instanceof String; // true
false
1 instanceof Object // false Object.create(null) instanceof Object // false
constructorFunc
必须是个对象。而且大部分状况要求是个构造函数(即要具备prototype
属性)// TypeError: Right-hand side of 'instanceof' is not an object 1 instanceof 1 // TypeError: Right-hand side of 'instanceof' is not callable 1 instanceof ({}) // TypeError: Function has non-object prototype 'undefined' in instanceof check ({}) instanceof (() => {})
intanceof
的缺陷不一样的全局执行上下文的对象和函数都是不相等的,因此对于跨全局执行上下文intanceof
就不能正常工做了。
<!DOCTYPE html> <html> <head></head> <body> <iframe src=""></iframe> <script type="text/javascript"> var iframe = window.frames[0]; var iframeArr = new iframe.Array(); console.log([] instanceof iframe.Array) // false console.log(iframeArr instanceof Array) // false console.log(iframeArr instanceof iframe.Array) // true </script> </body> </html>
Symbol.hasInstance
函数instanceof
操做符判断构造函数constructorFunc
的prototype
属性是否在对象object
的原型链上。可是能够利用Symbol.hasInstance
自定义instanceof
操做逻辑。
var obj = {} // 自定义Symbol.hasInstance方法 Object.defineProperty(obj, Symbol.hasInstance, { value: function() { return true; } }); 1 instanceof obj // true
固然了这个举例没有任何实际意义。只是说明下Symbol.hasInstance
的功能。Symbol.hasInstance
本意是自定义构造函数判断实例对象的方式,不要改变instanceof
的含义。
instanceof
内部逻辑综上能够梳理instanceof
内部逻辑
object instanceof constructorFunc
constructorFunc
不是个对象,或则是null
,直接抛TypeError
异常;constructorFunc[Symbole.hasInstance]
方法,则返回!!constructorFunc[Symbole.hasInstance](object )
constructorFunc
不是函数,直接抛TypeError
异常;遍历object
的原型链,逐个跟constructorFunc.prototype
属性比较:
object
没有原型,则直接返回false
;constructorFunc.prototype
不是对象,则直接抛TypeError
异常。ES5引入了方法Array.isArray
专门用于数组类型判断。Object.prototype.toString
和instanceof
都不够严格
var arr = [] Object.defineProperty(Array.prototype, Symbol.toStringTag, { get() { return 'myArray' } }) console.log(Object.prototype.toString.call(arr)); // [object myArray] console.log(Array.isArray(arr)); // true console.log(Array.prototype instanceof Array); // false console.log(Array.isArray(Array.prototype)); // true
不过现实状况下基本都是利用Object.prototype.toString
做为Array.isArray
的polyfill:
if (!Array.isArray) { Array.isArray = function(arg) { return Object.prototype.toString.call(arg) === '[object Array]'; }; }
prototype
属性类型判断内置的对象Number
, String
, Boolean
, Object
, Function
, Date
, RegExp
, Array
都是各自类型对象的构造函数,而且他们的prototype
属性都是各自实例对象的原型。可是这些内置对象的prototype
属性又是什么类型呢?
Number.prototype
Number.prototype
也是个数字,相似Number(0)
,可是Number.prototype
并非Number
的实例。
var prototype = Number.prototype console.log(prototype == 0); // true console.log(prototype instanceof Number); // false console.log(Object.prototype.toString.call(protoype)); // [object Number]
String.prototype
String.prototype
也是字符串,相似""
,可是String.prototype
并非String
的实例。
var prototype = String.prototype console.log(prototype == ''); // true console.log(prototype instanceof String); // false console.log(Object.prototype.toString.call(prototype)); // [object String]
Boolean.prototype
Boolean.prototype
也是Boolean,相似false
,可是Boolean.prototype
并非Boolean
的实例。
var prototype = Boolean.prototype console.log(prototype == false); // true console.log(prototype instanceof Boolean); // false console.log(Object.prototype.toString.call(prototype)); // [object Boolean]
Object.prototype
Object.prototype
也是Object,相似Object.create(null)
的值(原型为null
的空对象),可是Object.prototype
并非Object
的实例。
var prototype = Object.prototype Object.getPrototypeOf(prototype); // null console.log(prototype instanceof Object); // false console.log(Object.prototype.toString.call(prototype)); // [object Object]
Function.prototype
Function.prototype
也是Function,是个空函数,可是Function.prototype
并非Function
的实例。
var prototype = Function.prototype console.log(prototype()) // undefined console.log(prototype instanceof Function); // false console.log(Object.prototype.toString.call(prototype)); // [object Function]
Array.prototype
Array.prototype
也是Array,是个空数组,可是Array.prototype
并非Array
的实例。
var prototype = Array.prototype console.log(prototype instanceof Array); // false console.log(Array.isArray(prototype)) // true console.log(Object.prototype.toString.call(prototype)); // [object Array]
RegExp.prototype
RegExp.prototype
并非RegExp
的实例。可是关于RegExp.prototype
是RegExp
仍是对象存在兼容性问题,有些浏览器下RegExp.prototype
也是RegExp,而且是个总返回true
的正则。
var prototype = RegExp.prototype console.log(prototype.test('hello')) // true console.log(prototype instanceof RegExp); // false // Chrome v84返回"[object Object]", IE返回"[object RegExp]" console.log(Object.prototype.toString.call(prototype)); //
整理自gitHub笔记:
关注公众号「前端开发博客」,回复1024,领取前端资料包