JS中資料型別檢測四種方式的優缺點

金色小芝麻發表於2020-04-05

對於資料型別檢測筆者之前寫過一篇typeofJS中資料型別檢測方法——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”

JS中資料型別檢測四種方式的優缺點

  • 4、優點:使用起來簡單,基本資料型別值基本上都可以有效檢測,引用資料型別值也可以檢測出來
  • 5、侷限性(特殊值)
    • 1)、NaN / Infinity 都是數字型別的,檢測結果都是“number”;
    • 2)、typeof null 的結果是“object”;
      • (這是瀏覽器的BUG:所有的值在計算中都以二進位制編碼儲存,瀏覽器中把前三位000的當作物件,而null的二進位制前三位是000,所以被識別為物件,但是他不是物件,他是空物件指標,是基本型別值)
    • 3)、typeof 普通物件/陣列物件/正則物件..., 結果都是“object”,這樣就無法基於typeof 區分是普通物件還是陣列物件``...等了
  • 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
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為要檢測的資料值
  • 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本身專門進行資料檢測的,所以是目前檢測資料型別比較好的方法

思維導圖

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"
複製程式碼

JS中資料型別檢測四種方式的優缺點

引用資料型別中

  • 陣列中:

上面說過只有基本資料型別的建構函式的原型上都有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  檢測資料型別 
複製程式碼

JS中資料型別檢測四種方式的優缺點

應用——隱性轉換

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);
}
複製程式碼

JS中資料型別檢測四種方式的優缺點

相關文章