typeof 和 instanceof講解以及最佳型別判斷實踐
說在前面
JavaScript作為前端當家的語言,其重要程度不言而喻,一些基礎的語法以及概念之前經常有看,但是隨看隨忘,過程中有一些自己覺得驚鴻一瞥的發現也隨著時間消失不見,特此開一個專題,把往往種種的體會分享出來,供自己回顧以及各位拿取。
開門見山
其實在閱讀一些原始碼或者封裝的程式碼時,經常會看到 使用 typeof
或者 instanceof
來進行一些判斷,有時不能很好地理解判斷的具體含義,下面通過分析這兩者的使用方式以及自身特性來加強理解。
typeof
語法: typeof operand
後面可以緊跟表示物件或者原始值的表示式
那麼他判斷的所有型別都是準確無誤的嘛?或者說,他返回的型別都是我們想看到的嗎?顯然不是,比如JavaScript
最初設計中的一個小“瑕疵”,
typeof null // 'object'
我們不能說他錯,但是我們在實際使用的過程中可不希望把null 判斷成為一個object,因為這樣會導致一些嚴重的錯誤。
用一句簡單的話來說 typeof
可以準確的判斷結果不為 object
的型別的資料,別急,後面我們會印證這個觀點。
下面我們一起來看一下typeof
關鍵字在判斷型別時的一些表現:
typeof 37 // 'number'
typeof Nan // 'number' 注意,特殊型別的number,not a number的number
typeof true // 'boolean'
typeof '1' // 'string'
typeof String('1') // 'string'
typeof Symbol() // 'symbol'
typeof undefined // 'undefined'
typeof function() {} // 'function'
typeof {a: 1} // 'object' 一直到這裡,所有得到的資料型別都是我們想得到的
------------------------------ ------------------我是分界線--------------------------------------------
var a = [1,2,3]
typeof a // 'object', 陣列是一種特殊的物件,判斷是否為陣列,需要使用Array.isArray方法
typeof null // 'object' 可以通過 operand === null 判斷是否為 null
typeof new Date() // 'object'
typeof new String('a')
// 'object',如果你列印過控制檯的資訊,你會看到得到的結果是 String {"a"},確實展示形式是物件,但是我們使用
// 過程中會將其理解為字串(當然他在使用過程中和字串無異,但是他在記憶體中確實是以String {"a"}存在)
從上述例子中可以看出typeof
對於一些建立型別的資料(使用了new 關鍵字)都會返回 object
,雖說返回了記憶體中正確的存在形式,但結果可能並不是我們想要得到的。所以說 typeof
關鍵字,返回的不為 object
型別的資料都是準確的,但是對於返回型別為 object
的資料,我們需要斟酌一下實際的結果。
延伸一刻:如果我們想判斷一個數據表示式是否為object
型別,要如何判斷?
// we got a referance named a
if (typeof a !== null && typeof a === 'object') {
return true
}
唉? 剛才不是說typeof
判斷的型別為object
的不準嗎?怎麼現在又這樣寫呢?其實根據上述結果,new String('123')
雖然我們想得到的結果是string
,但是說他是一個object
卻沒有任何問題!
instanceof
語法: object instanceof constructor
用來檢測constructor.protype
是否 存在於引數 object
的原型鏈上。一定注意,後面跟的是一個建構函式。
一言以蔽之:可以準確判斷複雜引用的資料型別,但不能正確判斷基礎資料型別。
var d = new Date()
d instanceof Date // true
var s = new String('cctv')
s instanceof String // true s在控制檯打印出來的結果是 String{"cctv"}
var str = 'cctv'
str instanceof String // false 這是怎麼回事? 參見下方分析。
Object.getPrototypeOf(str) === String.prototype
// true 確實符合了概念,看似存在於str的原型鏈上,但是str自身並不是一個 object
由此可以判斷,instanceof
在內部實現中,會判斷 instanceof
左側是否為 object
。如果不是,返回 false
。
再有,原型鏈是具有傳遞性的:
function C() {}
var o = new C();
o instanceof C // true Object.getPrototypeOf(o) === C.prototype
C.prototype instanceof Object // true Object.getPrototypeof(C.prototype) === Object.prototype
o instanceof Object // true 原型鏈的傳遞性
由此可以分析得出,原生js
在實現 instanceof
的時候,會迴圈遍歷左側object
的 Object.getPrototypeOf('object')
屬性,並與 constructor.prototype
進行對比,如果相等,則返回 true,如果不相等則繼續排查,直到 Object.getPrototypeOf('object')
的結果為 null
則返回 false
。
綜上二者提出的概念,可以簡單的實現一個 自己的instanceof
,網上例子很多,這裡不貼程式碼了,根據我的經驗,貼上來程式碼大家很少會自己實踐,just try it.
最佳型別判斷實踐
可曾聽聞十三太保之 toString
,這是個很神奇的方法,每個物件都繼承了來自object
的這個是屬性,他可以被重寫來完成一些特殊的任務,這裡主要介紹他的預設用法,返回 [object class]
,其中class
是該物件的類,可以為 Object,Number,Function,Date,Window,HTMLDocument
等。
function getType(obj){
// 對於typeof返回結果是object的,再進行如下的判斷,正則返回結果
return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');
}
getType(window) // Window
getType(new Date()) // Date
getType(document) // HTMLDocument
參考網站: