對於數據類型檢測筆者以前寫過一篇typeof
的JS中數據類型檢測方法——typeof,今天就整理下JS中數據類型檢測的四種方式的區別:javascript
tyepof [value]
:檢測數據類型的運算符[example] instanceof [class]
: 檢測某一個實例是否屬於這個類[example].constructor===[class]
:檢測實例和類關係的,從而檢測數據類型Object.prototype.toString.call([value])
:檢測數據類型tyepof [value]
typeof
檢測的結果首先是一個字符串;「number」
、「string」
、「boolean」
、「undefined」
、「object」
、「function」
、「symbol」
、「bigint」
)NaN
/ Infinity
都是數字類型的,檢測結果都是「number」
;typeof null
的結果是「object」
;
BUG
:全部的值在計算中都以二進制編碼儲存,瀏覽器中把前三位000
的看成對象,而null
的二進制前三位是000
,因此被識別爲對象,可是他不是對象,他是空對象指針,是基本類型值)typeof
普通對象/數組對象/正則對象...
, 結果都是「object」
,這樣就沒法基於typeof
區分是普通對象
仍是數組對象``...
等了
- 已知有一個變量x,可是咱們沒法確認其數據類型,咱們須要有一個判斷操做:當x的類型是對象的時候(什麼對象均可以),則處理對應的事情
if (typeof x == "object") {
//=>null檢測結果也會是"object",因此結果是"object"不必定是對象,還多是null呢
...
}
複製代碼
能夠用👇的條件進行判斷java
if (x != null && typeof x == "object") {
// ...
}
複製代碼
console.log(typeof []); //=>"object"面試
console.log(typeof typeof typeof []); //=>"string"數組
需注意:瀏覽器
因爲`typeof`返回的結果永遠是一個字符串(字符串中包含了對應的類型),因此連續出現`兩個及兩個以上typeof檢測`的時候,最後結果都是` "string"` bash
true
實例 instanceof 類
TRUE
,不屬於返回FALSE
typeof
沒法細分對象類型的缺點(想檢測這個值是否爲數組,只須要看他是否爲Array類的實例便可)let arr = [10, 20];
console.log(typeof arr); //=>"object"
console.log(arr instanceof Array); //=>true
console.log(arr instanceof RegExp); //=>false
console.log(arr instanceof Object); //=>true 不論是數組對象仍是正則對象,都是Object的實例,檢測結果都是TRUE,因此沒法基於這個結果判斷是否爲普通對象
複製代碼
// instanceof檢測的實例必須都是引用數據類型的,它對基本數據類型值操做無效
console.log(10 instanceof Number); //=>false
console.log(new Number(10) instanceof Number); //=>true
// instanceof檢測機制:驗證當前類的原型prototype是否會出如今實例的原型鏈__proto__上,只要在它的原型鏈上,則結果都爲TRUE
function Fn() {}
Fn.prototype = Object.create(Array.prototype);
let f = new Fn;
console.log(f instanceof Array); //=>true f其實不是數組,由於它連數組的基本結構都是不具有的
複製代碼
注意️:它自己不能完成數據類型檢測,只是利用它(檢測某個實例屬否屬於這個類的)特徵來完成數據檢測函數
constructor
的屬性值是否是預估的類(利用他的實例數據類型檢測)實例.constructor === 類
TRUE
,不屬於返回FALSE
實例.constructor
通常都等於 類.prototype.constructor
也就是當前類自己(前提是你的 constructor
並無被破壞)Object
)constructor
,也會形成檢測的結果不許確(Object
)JS
中的constructor
是不被保護的(用戶能夠本身隨便改),這樣基於constructor
檢測的值存在不肯定性(可是項目中,基本沒有人會改內置類的constructor
)let arr = [],
obj = {},
num = 10;
console.log(arr.constructor === Array); //=>true
console.log(arr.constructor === Object); //=>false
console.log(obj.constructor === Object); //=>true
console.log(num.constructor === Number); //=>true
複製代碼
注意:它自己不能完成數據類型檢測,利用他的實例數據類型檢測(不能重定向)post
這個方法在Object的原型上ui
Object.prototype
上的toString
方法,讓toString
方法執行,而且基於call
讓方法中的this
指向檢測的數據值,這樣就能夠實現數據類型檢測了toString
方法;Object.prototype
上的toString
是用來返回當前實例所屬類的信息(檢測數據類型的),其他的都是轉換爲字符串的對象實例.toString()
:toString
方法中的THIS
是對象實例,也就是檢測它的數據類型,也就是THIS
是誰,就是檢測誰的數據類型Object.prototype.toString.call([value])
因此咱們是把toString
執行,基於call
改變this
爲要檢測的數據值Object.prototype.toString.call(10)
({}).toString.call(10)
({}).toString===Object.prototype.toString
複製代碼
let class2type = {};
let toString = class2type.toString; //=>Object.prototype.toString
console.log(toString.call(10)); //=>"[object Number]"
console.log(toString.call(NaN)); //=>"[object Number]"
console.log(toString.call("xxx")); //=>"[object String]"
console.log(toString.call(true)); //=>"[object Boolean]"
console.log(toString.call(null)); //=>"[object Null]"
console.log(toString.call(undefined)); //=>"[object Undefined]"
console.log(toString.call(Symbol())); //=>"[object Symbol]"
console.log(toString.call(BigInt(10))); //=>"[object BigInt]"
console.log(toString.call({xxx:'xxx'})); //=>"[object Object]"
console.log(toString.call([10,20])); //=>"[object Array]"
console.log(toString.call(/^\d+$/)); //=>"[object RegExp]"
console.log(toString.call(function(){})); //=>"[object Function]"
複製代碼
注意:此方法是基於JS自己專門進行數據檢測的,因此是目前檢測數據類型比較好的方法this
JQ 中對於數據類型檢測,筆者理解的主要仍是利用Object.prototype.toString.call()
方法,也能夠說是Object.prototype.toString.call()
能夠完成數據類型檢測的原理解析;
function toType(obj) {
let class2type = {},
toString = class2type.toString, //=>Object.prototype.toString 檢測數據類型
arr = "Boolean Number String Function Array Date RegExp Object Error Symbol".split(" ");
arr.forEach(item => {
class2type["[object " + item + "]"] = item.toLowerCase();
})
/* console.log(class2type); { [object Boolean]: "boolean", [object Number]: "number", [object String]: "string" ...... } */
//傳遞給個人是null/undefined,直接返回 "null"/"undefined"
if (obj == null) {
return "" + obj;
}
// typeof obj === "object" || typeof obj === "function" =>引用數據類型
// => 若是是基本數據類型值,檢測數據類型使用typeof就能夠
// => 若是是引用數據類型值,則基於對象的toString就能夠
// => toString.call(obj) 檢測當前值的數據類型 "[object Xxx]"
// => class2type["[object Xxx]"] 當上一步生成的對象中,基於對應的屬性名,找到屬性值(所屬的數據類型),若是沒有則返回 "object"
return typeof obj === "object" || typeof obj === "function" ? class2type[toString.call(obj)] || "object" : typeof obj;
};
console.log(toType(1)); //=>"number"
console.log(toType(NaN)); //=>"number"
console.log(toType([])); //=>"array"
console.log(toType(/^\d+$/)); //=>"regexp"
console.log(toType({})); //=>"object"
console.log(toType(null)); //=>"object"
console.log(toType()); //=>"object"
複製代碼
上面咱們已經闡述完數據類型檢測四種方式的優缺點;
咱們提到了在Object
原型上的toString
方法,這裏順便提一下另一個在Object
原型上的valueOf
方法
Object
原型上的valueOf
方法每一種數據類型的構造函數的原型上都有
toString
方法;每一種基本數據類型的構造函數的原型上都有
valueOf
方法;Object.prototype
的原型上也有valueOf
方法;
let num1 = 10,
num2 = new Number(10);
console.log(num1);//=> 10
console.log(num1.valueOf());//=> 10
console.log(num1.toString());//=> "10"
console.log(num2);//=>Number {10}
console.log(num2.valueOf());//=> 10
console.log(num2.toString());//=> "10"
複製代碼
上面說過只有基本數據類型的構造函數的原型上都有valueOf
方法;Object.prototype
的原型上也有valueOf
方法;
因此數組的構造函數的原型上是沒有這個方法的,咱們用數組調取valueOf
方法,其實是經過數組的原型鏈查找到Object.prototype
的原型上,調取valueOf
的方法;
let arr = [10, 20],
obj = {
xxx: 'xxx'
};
console.log(arr);
console.log(arr.valueOf()); //=>調取的是 Object.prototype.valueOf 原始值
console.log(arr.toString()); //=>調取的是 Array.prototype.toString 轉換字符串
console.log(obj);
console.log(obj.valueOf()); //=>調取的是 Object.prototype.valueOf 原始值
console.log(obj.toString()); //=>調取的是 Object.prototype.toString 檢測數據類型
複製代碼
let num1 = 10,
num2 = new Number(10);
let arr = [10, 20],
obj = {
xxx: 'xxx'
};
複製代碼
例如:
ALERT
會把全部要輸出的值轉換爲字符串(隱性轉換),像這種隱性轉換爲字符串的還有不少,例如:字符串拼接、把對象轉換爲數字(也是先轉換爲字符串)...
- =>num1.valueOf() 先獲取原始值
- =>[[PrimitiveValue]].toString() 把獲取的原始值轉換爲字符串
原題以下:
//=> 下面代碼a在什麼值狀況下會輸出1
var a = ?;
if (a == 1 && a == 2 && a == 3) {
console.log(1);
}
複製代碼
其中一種解題思路是能夠利用valueOf
獲取原始值
var a = {
n : 0,
valueOf(){
return ++this.n;
}
};
if (a == 1 && a == 2 && a == 3) {
console.log(1);
}
複製代碼