對於資料型別檢測筆者之前寫過一篇typeof
的JS中資料型別檢測方法——typeof,今天就整理下JS中資料型別檢測的四種方式的區別:
tyepof [value]
:檢測資料型別的運算子[example] instanceof [class]
: 檢測某一個例項是否屬於這個類[example].constructor===[class]
:檢測例項和類關係的,從而檢測資料型別Object.prototype.toString.call([value])
:檢測資料型別
一、typeof
- 1、定義:用來檢測資料型別的運算子
- 2、語法:
tyepof [value]
- 3、返回值:
typeof
檢測的結果首先是一個字串;- 字串中包含了對應的資料型別(例如:
“number”
、“string”
、“boolean”
、“undefined”
、“object”
、“function”
、“symbol”
、“bigint”
)
- 4、優點:使用起來簡單,基本資料型別值基本上都可以有效檢測,引用資料型別值也可以檢測出來
- 5、侷限性(特殊值)
- 1)、
NaN
/Infinity
都是數字型別的,檢測結果都是“number”
; - 2)、
typeof null
的結果是“object”
;- (這是瀏覽器的
BUG
:所有的值在計算中都以二進位制編碼儲存,瀏覽器中把前三位000
的當作物件,而null
的二進位制前三位是000
,所以被識別為物件,但是他不是物件,他是空物件指標,是基本型別值)
- (這是瀏覽器的
- 3)、
typeof
普通物件/陣列物件/正則物件...
, 結果都是“object”
,這樣就無法基於typeof
區分是普通物件
還是陣列物件``...
等了
- 1)、
- 6、應用場景
- 已知有一個變數x,但是我們無法確認其資料型別,我們需要有一個判斷操作:當x的型別是物件的時候(什麼物件都可以),則處理對應的事情
if (typeof x == "object") {
//=>null檢測結果也會是"object",所以結果是"object"不一定是物件,還可能是null呢
...
}
複製程式碼
可以用?的條件進行判斷
if (x != null && typeof x == "object") {
// ...
}
複製程式碼
- 7、練習題
console.log(typeof []); //=>"object"
console.log(typeof typeof typeof []); //=>"string"
需注意:
由於`typeof`返回的結果永遠是一個字串(字串中包含了對應的型別),所以連續出現`兩個及兩個以上typeof檢測`的時候,最後結果都是` "string"`
二、instanceof
- 1、定義:用來檢測某個例項是否屬於這個類
- 當前類的原型只要出現在了例項的原型鏈上就返回
true
- 當前類的原型只要出現在了例項的原型鏈上就返回
- 2、語法:
例項 instanceof 類
- 3、屬於返回
TRUE
,不屬於返回FALSE
- 4、優點:
- 基於這種方式,可以彌補
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,所以無法基於這個結果判斷是否為普通物件
複製程式碼
- 5、侷限性:
- 1)、要求檢測的例項必須是物件資料型別的
- 2)、基本資料型別的例項是無法基於它檢測出來的
- 字面量方式建立的不能檢測
- 建構函式建立的就可以檢測
- 3)、不管是陣列物件韓式正則物件,都是 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
- 1、定義:判斷當前的例項的
constructor
的屬性值是不是預估的類(利用他的例項資料型別檢測) - 2、語法:
例項.constructor === 類
- 3、返回值:屬於返回
TRUE
,不屬於返回FALSE
- 4、優點:
例項.constructor
一般都等於類.prototype.constructor
也就是當前類本身(前提是你的constructor
並沒有被破壞)- 能檢測基本資料型別
- 5、侷限性:
- 1)、不能夠給當前類的原型進行重定向,會造成檢測的結果不準確(
Object
) - 2)、不能夠給當前例項增加私有屬性
constructor
,也會造成檢測的結果不準確(Object
) - 3)、非常容易被修改,因為
JS
中的constructor
是不被保護的(使用者可以自己隨便改),這樣基於constructor
檢測的值存在不確定性(但是專案中,基本沒有人會改內建類的constructor
)
- 1)、不能夠給當前類的原型進行重定向,會造成檢測的結果不準確(
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
複製程式碼
注意:它本身不能完成資料型別檢測,利用他的例項資料型別檢測(不能重定向)
四、Object.prototype.toString.call()
這個方法在Object的原型上
- 1、定義:找到
Object.prototype
上的toString
方法,讓toString
方法執行,並且基於call
讓方法中的this
指向檢測的資料值,這樣就可以實現資料型別檢測了 - 2、原理:
- 1.每一種資料型別的建構函式的原型上都有
toString
方法; - 2.除了
Object.prototype
上的toString
是用來返回當前例項所屬類的資訊(檢測資料型別的),其餘的都是轉換為字串的 - 3.
物件例項.toString()
:toString
方法中的THIS
是物件例項,也就是檢測它的資料型別,也就是THIS
是誰,就是檢測誰的資料型別 - 4.
Object.prototype.toString.call([value])
所以我們是把toString
執行,基於call
改變this
為要檢測的資料值
- 1.每一種資料型別的建構函式的原型上都有
- 3、使用方法:
- Object.prototype.toString.call(被檢測的例項)
- ({}).toString.call(被檢測的例項)
Object.prototype.toString.call(10)
({}).toString.call(10)
({}).toString===Object.prototype.toString
複製程式碼
- 4、返回值:
- 是一個字串“[Object 當前被檢測例項所屬的類]”
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]"
複製程式碼
- 5、優點:
- 專門用來檢測資料型別的方法,基本上不存在侷限性的資料型別檢測方式
- 基於他可以有效的檢測任何資料型別的值
- 6、侷限性:
- 1)、只能檢測內建類,不能檢測自定義類
- 2)、只要是自定義類返回的都是‘[Object Object]’
注意:此方法是基於JS本身專門進行資料檢測的,所以是目前檢測資料型別比較好的方法
思維導圖
上面我們已經闡述完資料型別檢測四種方式的優缺點;
我們提到了在Object
原型上的toString
方法,這裡順便提一下另外一個在Object
原型上的valueOf
方法
散知識:Object
原型上的valueOf
方法
每一種資料型別的建構函式的原型上都有
toString
方法;每一種基本資料型別的建構函式的原型上都有
valueOf
方法;Object.prototype
的原型上也有valueOf
方法;
基本資料型別中:
- valueOf:是獲取原始值 [[PrimitiveValue]]
- toString:是將原始值轉換為字串
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 && a == 2 && a == 3
原題如下:
//=> 下面程式碼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);
}
複製程式碼