1、typeof
typeof
操做符惟一的目的就是檢查數據類型php
類型 | typeof 結果 | |
---|---|---|
基本類型 | undefined | "undefined" |
Boolean | "boolean" | |
Number | "number" | |
String | "string" | |
BigInt (ECMAScript 2020 新增) | "bigint" | |
Symbol | "symbol" | |
null | "object" | |
引用類型 | Object(Object、Array、Map、Set等) | "object" |
Function | "function" |
因此,但咱們使用 typeof
來判斷引用類型變量時,不管是什麼類型的變量,它都會返回 Object
。html
爲此,引入了instanceof
。前端
2、instanceof
instanceof
與 typeof
相比,instanceof
方法要求開發者明確的確認對象爲某特定類型。即 instanceof
用於判斷引用類型屬於哪一個構造函數的方法。git
var arr = [] arr instanceof Array // true typeof arr // "object" // typeof 是沒法判斷類型是否爲數組的
instanceof
操做符檢測過程當中也會將繼承關係考慮在內,因此instanceof
能夠在繼承關係中用來判斷一個實例是否屬於它的父類型。github
// 判斷 f 是不是 Foo 類的實例 , 而且是不是其父類型的實例 function Aoo(){} function Foo(){} //JavaScript 原型繼承 Foo.prototype = new Aoo(); var foo = new Foo(); console.log(foo instanceof Foo) // true console.log(foo instanceof Aoo) // true
f instanceof Foo
的判斷邏輯是:面試
- f 的
__proto__
一層一層往上,是否對應到Foo.prototype
- 再往上,看是否對應着
Aoo.prototype
- 再試着判斷
f instanceof Object
即 instanceof
能夠用於判斷多層繼承關係。算法
3、instanceof 的內部實現原理
instanceof 的內部實現機制是:經過判斷對象的原型鏈上是否能找到對象的 prototype
,來肯定 instanceof
返回值數組
1. 內部實現原理
// instanceof 的內部實現 function instance_of(L, R) {//L 表左表達式,R 表示右表達式,即L爲變量,R爲類型 // 取 R 的顯示原型 var prototype = R.prototype // 取 L 的隱式原型 L = L.__proto__ // 判斷對象(L)的類型是否嚴格等於類型(R)的顯式原型 while (true) { if (L === null) { return false } // 這裏重點:當 prototype 嚴格等於 L 時,返回 true if (prototype === L) { return true } L = L.__proto__ } }
instanceof
運算符用來檢測 constructor.prototype
是否存在於參數 object
的原型鏈上。瀏覽器
看下面一個例子,instanceof
爲何會返回 true
?很顯然,an
並非經過 Bottle()
建立的。ecmascript
function An() {} function Bottle() {} An.prototype = Bottle.prototype = {}; let an = new An(); console.log(an instanceof Bottle); // true
這是由於 instanceof
關心的並非構造函數,而是原型鏈。
an.__proto__ === An.prototype; // true An.prototype === Bottle.prototype; // true // 即 an.__proto__ === Bottle.prototype; // true
即有 an.__proto__ === Bottle.prototype
成立,因此 an instanceof Bottle
返回了 true
。
因此,按照 instanceof
的邏輯,真正決定類型的是 prototype
,而不是構造函數。
2. Object.prototype.toString(擴展)
還有一個不錯的判斷類型的方法,就是 Object.prototype.toString
,咱們能夠利用這個方法來對一個變量的類型來進行比較準確的判斷
默認狀況下(不覆蓋 toString
方法前提下),任何一個對象調用 Object
原生的 toString
方法都會返回 "[object type]"
,其中 type
是對象的類型;
let obj = {}; console.log(obj); // {} console.log(obj.toString()); // "[object Object]"
[[Class]]
每一個實例都有一個 [[Class]]
屬性,這個屬性中就指定了上述字符串中的 type
(構造函數名)。 [[Class]]
不能直接地被訪問,但一般能夠間接地經過在這個值上借用默認的 Object.prototype.toString.call(..)
方法調用來展現。
Object.prototype.toString.call("abc"); // "[object String]" Object.prototype.toString.call(100); // "[object Number]" Object.prototype.toString.call(true); // "[object Boolean]" Object.prototype.toString.call(null); // "[object Null]" Object.prototype.toString.call(undefined); // "[object Undefined]" Object.prototype.toString.call([1,2,3]); // "[object Array]" Object.prototype.toString.call(/\w/); // "[object RegExp]"
能夠經過 Object.prototype.toString.call(..)
來獲取每一個對象的類型。
function isFunction(value) { return Object.prototype.toString.call(value) === "[object Function]" } function isDate(value) { return Object.prototype.toString.call(value) === "[object Date]" } function isRegExp(value) { return Object.prototype.toString.call(value) === "[object RegExp]" } isDate(new Date()); // true isRegExp(/\w/); // true isFunction(function(){}); //true
或者可寫爲:
function generator(type){ return function(value){ return Object.prototype.toString.call(value) === "[object "+ type +"]" } } let isFunction = generator('Function') let isArray = generator('Array'); let isDate = generator('Date'); let isRegExp = generator('RegExp'); isArray([])); // true isDate(new Date()); // true isRegExp(/\w/); // true isFunction(function(){}); //true
Symbol.toStringTag
Object.prototype.toString
方法可使用 Symbol.toStringTag
這個特殊的對象屬性進行自定義輸出。
舉例說明:
let bottle = { [Symbol.toStringTag]: "Bottle" }; console.log(Object.prototype.toString.call(bottle)); // [object Bottle]
大部分和環境相關的對象也有這個屬性。如下輸出可能因瀏覽器不一樣而異:
// 環境相關對象和類的 toStringTag: console.log(window[Symbol.toStringTag]); // Window console.log(XMLHttpRequest.prototype[Symbol.toStringTag]); // XMLHttpRequest console.log(Object.prototype.toString.call(window)); // [object Window] console.log(Object.prototype.toString.call(new XMLHttpRequest())); // [object XMLHttpRequest]
輸出結果和 Symbol.toStringTag
(前提是這個屬性存在)同樣,只不過被包裹進了 [object ...]
裏。
因此,若是但願以字符串的形式獲取內置對象類型信息,而不只僅只是檢測類型的話,能夠用這個方法來替代 instanceof
。
3. 總結
適用於 | 返回 | |
---|---|---|
typeof | 基本數據類型 | string |
instanceof | 任意對象 | true/false |
Object.prototype.toString |
基本數據類型、內置對象以及包含 Symbol.toStringTag 屬性的對象 |
string |
Object.prototype.toString
基本上就是一加強版 typeof
。
instanceof
在涉及多層類結構的場合中比較實用,這種狀況下須要將類的繼承關係考慮在內。
4、null爲何被typeof錯誤的判斷爲了'object'
// JavaScript 誕生以來便如此 typeof null === 'object';
在 JavaScript 最初的實現中,JavaScript 中的值是由一個表示類型的標籤和實際數據值表示的。對象的類型標籤是 0。因爲 null
表明的是空指針(大多數平臺下值爲 0x00),所以,null 的類型標籤是 0,typeof null
也所以返回 "object"
。(參考來源)
曾有一個 ECMAScript 的修復提案(經過選擇性加入的方式),但被拒絕了。該提案會致使 typeof null === 'null'
。
若是用 instanceof
來判斷的話:
null instanceof null // Uncaught TypeError: Right-hand side of 'instanceof' is not an object