關注『前端開發博客』公衆號,回覆 加羣,加入高薪前端羣
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,領取前端資料包