首頁 > 軟體

詳解JavaScript如何準確獲取任意變數的資料型別

2022-06-27 10:00:37

js是弱型別語言,或者說是動態語言,在定義變數時我們可以不提前宣告變數的型別,也可以在變數宣告後賦予不同型別的值。變數的型別會在程式執行的過程中被確定。對於開發者來說,雖然可以不用頻繁的定義不同的資料型別,但是如果是比較複雜的專案,或者接手他人的專案,發現一個變數到處被使用並且賦予不同型別的值,也許頭都大了。變數型別的不確定還可能出現執行時報錯:xxx is not a function,根本原因就是不同的資料型別所具有的方法是不一樣的,也許有的大家都有,因此不報錯;但是如果是特有的,或者有差異的,不判斷變數的型別就直接呼叫,就有可能出現生產bug。那麼應該如何才能獲取準確的變數型別呢?

判斷型別常用的方法

typeof

js內建的一個運運算元,返回一個字串,表示運算元的型別。可用於number、string、boolean等值型別,function函數,object等參照型別,如果是判斷物件型別,則只能得到object,無法得到具體的值。

console.log(typeof ''); // string
console.log(typeof (() => {})); // function
console.log(typeof Symbol()); // symbol
console.log(typeof undefined); // undefined
console.log(typeof null); // object
console.log(typeof []); // object
console.log(typeof {}); // object

我們可以看到,陣列、物件通過typeof都是返回了object,無法細分;其中值得注意的是typeof null結果也是object。這是因為在js的最初實現中,js中的值是由一個表示型別的標籤以及實際資料值組合表示的。物件型別的標籤是0,我們都知道null表示空,在大多數平臺下其值為0x00,因此物件和null的型別標籤是一樣的。型別標籤一樣,但是null卻沒有正常物件的屬性以及方法。

instanceof

instanceof也是js內建的一個運運算元,用於判斷兩個引數之間是否存在聯絡,實現原理是檢測建構函式的prototype屬性是否出現在某個範例物件的原型鏈上。如果是,則返回true;否則,返回false。

console.log({} instanceof Array); // false
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log([] instanceof Object); // true

instanceof是判斷兩個引數,並不會直接獲取變數的型別,可以通過列舉的方式簡介獲取。

toString

每一個物件都有一個toString()方法,因為每個物件都會繼承自Object。可能在大多數人的印象中,toString()只是用來返回一個字串,並沒有什麼特別的地方,那是因為你看到的toString()基本上都是被重寫後的,原始的toString()是可以用來獲取物件的型別的。

舉個例子:

const o = new Object();
o.toString(); // [object Object]
const arr = [1,2,3]; // 陣列也會繼承自Object
arr.toString(); // '1,2,3',因為陣列重寫了toString

每個物件都能通過Object.prototype.toString.call(thisArg)或者Object.prototype.toString.apply(thisArg)來獲取其型別:

console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call([])); // [object Array]
console.log(Object.prototype.toString.call({})); // [object Object]
console.log(Object.prototype.toString.call(() => {})); // [object Function]

每次都呼叫js內建的API還是有點麻煩,所以我們還是封裝一個函數getType

封裝一個獲取型別的函數

typeof + instanceof(不推薦)

之前我們也提到了typeof可以獲取部分資料型別,instanceof可以用列舉來表示剩下的型別,那麼這兩者結合是不是就可以實現getType了呢?

function getType(obj) {
    if (typeof obj === 'object') {
        if (obj instanceof Array) return 'array';
        if (obj instanceof Map) return 'map';
        if (obj instanceof Set) return 'set';
        if (obj instanceof RegExp) return 'regexp';
        // 儘可能列舉型別,最後兜底返回object
        return 'object'
    }
    return (typeof obj);
}
console.log(getType(null)); // object
console.log(getType(undefined)); // undefined
console.log(getType('')); // string
console.log(getType([])); // array
console.log(getType(/a/)); // regexp
console.log(getType(new WeakMap())); // object
console.log(getType(new WeakSet())); // object

使用此方式雖然可以實現getType,但是存在弊端;

  • 由於是列舉,導致程式碼長度可能會很長
  • 列舉的資料型別可能有限,未能覆蓋常用資料型別
  • 如果要新增型別,則需要修改程式碼

toString

關於toString()的用法剛剛已經說了,我們以此來實現一個getType

function getType(obj) {
    const originType = Object.prototype.toString.call(obj); // 獲取型別。返回格式為[object type],需要提取出type的值
    const firstSpaceIndex = originType.indexOf(' '); // 找到第一個空格所在下標
    const type = originType.slice(firstSpaceIndex + 1, -1); // 跳過空格,擷取到倒數第一個字元(不含最後一個)
    return type.toLowerCase(); // 轉換為小寫
}
console.log(getType(null)); // null
console.log(getType(undefined)); // undefined
console.log(getType('')); // string
console.log(getType([])); // array
console.log(getType(/a/)); // regexp
console.log(getType(new WeakMap())); // weakmap
console.log(getType(new WeakSet())); // weakset

實現getType我們也可以藉助於運算元組的方式:

function getType(obj) {
    const originType = Object.prototype.toString.call(obj); // 獲取型別。返回格式為[object type],需要提取出type的值
    const tmpType = originType.split(' ')[1]; // 根據空格分割,並取第二個
    const type = tmpType.slice(0, -1); // 從0擷取到倒數第一個字元(不含最後一個)
    return type.toLowerCase(); // 轉換為小寫
}
console.log(getType(null)); // null
console.log(getType(undefined)); // undefined
console.log(getType('')); // string
console.log(getType([])); // array
console.log(getType(/a/)); // regexp
console.log(getType(new WeakMap())); // weakmap
console.log(getType(new WeakSet())); // weakset

兩者實現結果是一致的,基本上實現了我們的既定目標,而且可以獲取到所有的資料型別。不同的是,運算元組比直接操作字串更消耗效能。

總結

實現一個獲取變數型別的方法,其實並不複雜,簡單來說就是使用了內建的API:Object.prototype.toString()。有時候,往往就是因為過於簡單而被我們忽略,其實越是簡單的方式,才是越有效的。

以上就是詳解JavaScript如何準確獲取任意變數的資料型別的詳細內容,更多關於JavaScript獲取變數資料型別的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com