typeof
通常被用於判斷一個變量的類型,咱們能夠利用 typeof
來判斷number
, string
, object
, boolean
, function
, undefined
, symbol
這七種類型,這種判斷能幫助咱們搞定一些問題,好比在判斷不是 object 類型的數據的時候,typeof
能比較清楚的告訴咱們具體是哪一類的類型。可是,很遺憾的一點是,typeof
在判斷一個 object的數據的時候只能告訴咱們這個數據是 object, 而不能細緻的具體到是哪種 object, 好比👉javascript
let s = new String('abc');
typeof s === 'object'// true
s instanceof String // true
複製代碼
要想判斷一個數據具體是哪種 object 的時候,咱們須要利用 instanceof
這個操做符來判斷,這個咱們後面會說到。java
來談談關於 typeof
的原理吧,咱們能夠先想一個頗有意思的問題,js 在底層是怎麼存儲數據的類型信息呢?或者說,一個 js 的變量,在它的底層實現中,它的類型信息是怎麼實現的呢?數組
其實,js 在底層存儲變量的時候,會在變量的機器碼的低位1-3位存儲其類型信息👉ide
but, 對於 undefined
和 null
來講,這兩個值的信息存儲是有點特殊的。函數
null
:全部機器碼均爲0ui
undefined
:用 −2^30 整數來表示spa
因此,typeof
在判斷 null
的時候就出現問題了,因爲 null
的全部機器碼均爲0,所以直接被當作了對象來看待。prototype
然而用 instanceof
來判斷的話👉code
null instanceof null // TypeError: Right-hand side of 'instanceof' is not an object
複製代碼
null
直接被判斷爲不是 object,這也是 JavaScript 的歷史遺留bug,能夠參考typeof。cdn
所以在用 typeof
來判斷變量類型的時候,咱們須要注意,最好是用 typeof
來判斷基本數據類型(包括symbol
),避免對 null 的判斷。
還有一個不錯的判斷類型的方法,就是Object.prototype.toString,咱們能夠利用這個方法來對一個變量的類型來進行比較準確的判斷
Object.prototype.toString.call(1) // "[object Number]"
Object.prototype.toString.call('hi') // "[object String]"
Object.prototype.toString.call({a:'hi'}) // "[object Object]"
Object.prototype.toString.call([1,'a']) // "[object Array]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(() => {}) // "[object Function]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(Symbol(1)) // "[object Symbol]"
複製代碼
以前咱們提到了 instanceof
來判斷對象的具體類型,其實 instanceof
主要的做用就是判斷一個實例是否屬於某種類型
let person = function () {
}
let nicole = new person()
nicole instanceof person // true
複製代碼
固然,instanceof
也能夠判斷一個實例是不是其父類型或者祖先類型的實例。
let person = function () {
}
let programmer = function () {
}
programmer.prototype = new person()
let nicole = new programmer()
nicole instanceof person // true
nicole instanceof programmer // true
複製代碼
這是 instanceof
的用法,可是 instanceof
的原理是什麼呢?根據 ECMAScript 語言規範,我梳理了一下大概的思路,而後整理了一段代碼以下
function new_instance_of(leftVaule, rightVaule) {
let rightProto = rightVaule.prototype; // 取右表達式的 prototype 值
leftVaule = leftVaule.__proto__; // 取左表達式的__proto__值
while (true) {
if (leftVaule === null) {
return false;
}
if (leftVaule === rightProto) {
return true;
}
leftVaule = leftVaule.__proto__
}
}
複製代碼
其實 instanceof
主要的實現原理就是隻要右邊變量的 prototype
在左邊變量的原型鏈上便可。所以,instanceof
在查找的過程當中會遍歷左邊變量的原型鏈,直到找到右邊變量的 prototype
,若是查找失敗,則會返回 false,告訴咱們左邊變量並不是是右邊變量的實例。
看幾個頗有趣的例子
function Foo() {
}
Object instanceof Object // true
Function instanceof Function // true
Function instanceof Object // true
Foo instanceof Foo // false
Foo instanceof Object // true
Foo instanceof Function // true
複製代碼
要想所有理解 instanceof
的原理,除了咱們剛剛提到的實現原理,咱們還須要知道 JavaScript 的原型繼承原理。
關於原型繼承的原理,我簡單用一張圖來表示
咱們知道每一個 JavaScript 對象均有一個隱式的 __proto__
原型屬性,而顯式的原型屬性是 prototype
,只有 Object.prototype.__proto__
屬性在未修改的狀況下爲 null 值。根據圖上的原理,咱們來梳理上面提到的幾個有趣的 instanceof
使用的例子。
Object instanceof Object
由圖可知,Object 的 prototype
屬性是 Object.prototype
, 而因爲 Object 自己是一個函數,由 Function 所建立,因此 Object.__proto__
的值是 Function.prototype
,而 Function.prototype
的 __proto__
屬性是 Object.prototype
,因此咱們能夠判斷出,Object instanceof Object
的結果是 true 。用代碼簡單的表示一下
leftValue = Object.__proto__ = Function.prototype;
rightValue = Object.prototype;
// 第一次判斷
leftValue != rightValue
leftValue = Function.prototype.__proto__ = Object.prototype
// 第二次判斷
leftValue === rightValue
// 返回 true
複製代碼
Function instanceof Function
和 Function instanceof Object
的運行過程與 Object instanceof Object
相似,故再也不詳說。
Foo instanceof Foo
Foo 函數的 prototype
屬性是 Foo.prototype
,而 Foo 的 __proto__
屬性是 Function.prototype
,由圖可知,Foo 的原型鏈上並無 Foo.prototype
,所以 Foo instanceof Foo
也就返回 false 。
咱們用代碼簡單的表示一下
leftValue = Foo, rightValue = Foo
leftValue = Foo.__proto = Function.prototype
rightValue = Foo.prototype
// 第一次判斷
leftValue != rightValue
leftValue = Function.prototype.__proto__ = Object.prototype
// 第二次判斷
leftValue != rightValue
leftValue = Object.prototype = null
// 第三次判斷
leftValue === null
// 返回 false
複製代碼
Foo instanceof Object
leftValue = Foo, rightValue = Object
leftValue = Foo.__proto__ = Function.prototype
rightValue = Object.prototype
// 第一次判斷
leftValue != rightValue
leftValue = Function.prototype.__proto__ = Object.prototype
// 第二次判斷
leftValue === rightValue
// 返回 true
複製代碼
Foo instanceof Function
leftValue = Foo, rightValue = Function
leftValue = Foo.__proto__ = Function.prototype
rightValue = Function.prototype
// 第一次判斷
leftValue === rightValue
// 返回 true
複製代碼
簡單來講,咱們使用 typeof
來判斷基本數據類型是 ok 的,不過須要注意當用 typeof
來判斷 null
類型時的問題,若是想要判斷一個對象的具體類型能夠考慮用 instanceof
,可是 instanceof
也可能判斷不許確,好比一個數組,他能夠被 instanceof
判斷爲 Object。因此咱們要想比較準確的判斷對象實例的類型時,能夠採起 Object.prototype.toString.call
方法。