JS 中判断数据类型的形式有很多
- typeof
- Object.prototype.toString
- instanceof
- Array.isArray
一、回顾
JS 数据类型分为根本类型和援用类型。
根本类型:
- undefined
- null
- Number
- String
- Boolean
- Symbol
援用类型
- Object
- Function
函数是一种非凡的对象,即可调用的对象。
二、typeof
2.1 语法
typeof
操作符能够辨别根本类型,函数和对象。
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
等。
2.2 原理
JS 是动静类型的变量,每个变量在存储时除了存储变量值外,还须要存储变量的类型。JS 里应用 32 位(bit)存储变量信息。低位的 1~3 个 bit 存储变量类型信息,叫做类型标签(type tag)
.... 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]]
外部办法则认为是函数。
如何辨认undefined
undefined
变量存储的是个非凡值 JSVAL_VOID
(0-2^30),typeof
外部判断如果一个变量存储的是这个非凡值,则认为是undefined
。
#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
辨别各种内置对象。
3.2 语法
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
不存在对应的援用类型,外部非凡解决了;
3.3 原理
外部属性[[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
也就不能失常工作了。
3.4 Object.prototype.toString
外部逻辑
综上能够总结 Object.prototype.toString
的外部逻辑:
- 如果实参是
undefined
, 则返回 ”[object Undefined]”; - 如果实参是
null
, 则返回 ”[object Null]”; - 把实参转成对象
-
获取对象的
Symbol.toStringTag
属性值subType
- 如果
subType
是个字符串,则返回[object subType]
- 否则获取对象的
[[Class]]
属性值type
,并返回[object type]
- 如果
四、instanceof
4.1 语法
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 (() => {})
4.2 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>
4.3 原理
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
的含意。
原型链
4.4 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
异样。
- 如果
五、内置的类型判断办法
5.1 Array.isArray
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
属性又是什么类型呢?
6.1 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]
6.2 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]
6.3 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]
6.4 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]
6.5 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]
6.6 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]
6.6 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 笔记:
- JS- 类型 & 类型判断
- JS- 操作符 -instanceof