JS中數據類型檢測四種方式的優缺點

對於數據類型檢測筆者以前寫過一篇typeofJS中數據類型檢測方法——typeof,今天就整理下JS中數據類型檢測的四種方式的區別:javascript

  • tyepof [value] :檢測數據類型的運算符
  • [example] instanceof [class] : 檢測某一個實例是否屬於這個類
  • [example].constructor===[class] :檢測實例和類關係的,從而檢測數據類型
  • Object.prototype.toString.call([value]):檢測數據類型

1、typeof

  • 一、定義:用來檢測數據類型的運算符
  • 二、語法:tyepof [value]
  • 三、返回值:
    • typeof 檢測的結果首先是一個字符串;
    • 字符串中包含了對應的數據類型(例如: 「number」「string」「boolean」「undefined」「object」「function」「symbol」「bigint」

  • 四、優勢:使用起來簡單,基本數據類型值基本上均可以有效檢測,引用數據類型值也能夠檢測出來
  • 五、侷限性(特殊值)
    • 1)、NaN / Infinity 都是數字類型的,檢測結果都是「number」;
    • 2)、typeof null 的結果是「object」;
      • (這是瀏覽器的BUG:全部的值在計算中都以二進制編碼儲存,瀏覽器中把前三位000的看成對象,而null的二進制前三位是000,因此被識別爲對象,可是他不是對象,他是空對象指針,是基本類型值)
    • 3)、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

2、instanceof

  • 一、定義:用來檢測某個實例是否屬於這個類
    • 當前類的原型只要出如今了實例的原型鏈上就返回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,因此沒法基於這個結果判斷是否爲普通對象 
複製代碼
  • 五、侷限性:
    • 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其實不是數組,由於它連數組的基本結構都是不具有的 
複製代碼

注意️:它自己不能完成數據類型檢測,只是利用它(檢測某個實例屬否屬於這個類的)特徵來完成數據檢測函數

3、constructor

  • 一、定義:判斷當前的實例的constructor的屬性值是否是預估的類(利用他的實例數據類型檢測)
  • 二、語法:實例.constructor === 類
  • 三、返回值:屬於返回TRUE,不屬於返回FALSE
  • 四、優勢:
    • 實例.constructor 通常都等於 類.prototype.constructor 也就是當前類自己(前提是你的 constructor 並無被破壞)
    • 能檢測基本數據類型
  • 五、侷限性:
    • 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 
複製代碼

注意:它自己不能完成數據類型檢測,利用他的實例數據類型檢測(不能重定向)post

4、Object.prototype.toString.call()

這個方法在Object的原型上ui

  • 一、定義:找到Object.prototype上的toString方法,讓toString方法執行,而且基於call讓方法中的this指向檢測的數據值,這樣就能夠實現數據類型檢測了
  • 二、原理:
    • 1.每一種數據類型的構造函數的原型上都有toString方法;
    • 2.除了Object.prototype上的toString是用來返回當前實例所屬類的信息(檢測數據類型的),其他的都是轉換爲字符串的
    • 3.對象實例.toString()toString方法中的THIS是對象實例,也就是檢測它的數據類型,也就是THIS是誰,就是檢測誰的數據類型
    • 4.Object.prototype.toString.call([value]) 因此咱們是把toString執行,基於call改變this爲要檢測的數據值
  • 三、使用方法:
    • Object.prototype.toString.call(被檢測的實例)
    • ({}).toString.call(被檢測的實例)
Object.prototype.toString.call(10)
({}).toString.call(10)
({}).toString===Object.prototype.toString
複製代碼
  • 四、返回值:
    • 是一個字符串「[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]" 
複製代碼
  • 五、優勢:
    • 專門用來檢測數據類型的方法,基本上不存在侷限性的數據類型檢測方式
    • 基於他能夠有效的檢測任何數據類型的值
  • 六、侷限性:
    • 1)、只能檢測內置類,不能檢測自定義類
    • 2)、只要是自定義類返回的都是‘[Object Object]’

注意:此方法是基於JS自己專門進行數據檢測的,因此是目前檢測數據類型比較好的方法this

思惟導圖

JQ 中基於數據類型檢測的方法——本身封裝

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方法;

基本數據類型中:

  • 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);
}
複製代碼

相關文章
相關標籤/搜索