欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript獲取數據類型的方法詳解

 更新時間:2024年02月05日 08:32:38   作者:FeCoder  
這篇文章給大家介紹了JavaScript獲取數據類型的方法,文中所介紹的所有知識點、代碼示例以及提供的解決方案,均不考慮?IE?瀏覽器,僅支持最新版本的?Chrome、Firefox、Edge?和?Safari?瀏覽器,需要的朋友可以參考下

說明

本文所介紹的所有知識點、代碼示例以及提供的解決方案,均不考慮 IE 瀏覽器,僅支持最新版本的 Chrome、Firefox、Edge 和 Safari 瀏覽器。

概述

前端開發(fā)過程中一個常見的功能是:檢測某個數據屬于什么類型,是字符串、數字、數組、還是對象等等。比如,我們定義了一個函數,并且支持傳參,往往就需要對傳入的參數進行數據類型檢測,然后根據檢測結果進行相應的處理,這時我們就必須知道如何準確的獲取數據的類型。在構思解決方案之前,我們首先需要回顧一下基礎知識,那就是在 JavaScript 中到底有幾種數據類型?

數據類型種類

這里所講的數據類型指的是 JavaScript 語言層面的數據類型,截至目前,共有 8 種類型,可分為【基本數據類型】和【引用數據類型】:

基本數據類型

  • 字符串( String
  • 數字( Number
  • 布爾值 ( Boolean
  • null
  • undefined
  • Symbol
  • BigInt

引用數據類型

  • 對象( Object, Array 等等 )

區(qū)別

上面提到的【基本數據類型】和【引用數據類型】有什么區(qū)別呢?

基本數據類型的值是保存在 “棧” 內存中的,它是可以直接訪問的,所有的讀寫操作都是直接作用于數據本身,中間沒有任何 “轉接” 行為。

引用數據類型的值是保存在 “堆” 內存中的,在 JavaScript 中是不允許直接訪問堆內存中的數據的,要想訪問就需要拿到它在堆內存中的地址,然后通過這個地址進行讀寫操作。

舉個例子:張三要跟李四溝通事情,基本數據類型就相當于,張三直接跟李四本人交流。而引用數據類型則相當于張三要跟 “代理人” 溝通,再由這個 “代理人” 把張三的需求轉述給李四,李四如有反饋,也必須通過 “代理人” 轉告給張三,張三和李四由始至終都不能直接溝通。

檢測方法

typeof 運算符

這是最簡單也是最常用的數據類型檢測方法,但同時它也不太 “靠譜”,為什么這樣說呢?可以先看看下面的代碼示例:

console.log( typeof "data" );          // string
console.log( typeof 123456 );          // number
console.log( typeof true );            // boolean
console.log( typeof function () {} );  // function
console.log( typeof Symbol() );        // symbol
console.log( typeof 100n );            // bigint
console.log( typeof undefined );       // undefined

console.log( "===================================" );

console.log( typeof null );            // object
console.log( typeof { a: "a" } );      // object    
console.log( typeof [ 1, 2, 3 ] );     // object

可以看到,對于前七種數據,能檢測出相應的類型,而后三種卻一律返回 object。前面曾提到,ArrayObject 都屬于引用數據類型,而 null 被認為是對空對象的引用,也歸屬于 Object 范疇,由此可見,typeof 是無法區(qū)分出引用數據類型的。

上面的示例中還有一個關鍵點,那就是 function 函數。函數實際上也是對象,它并不代表一種數據類型,但它卻非常特殊。函數擁有對象的所有能力,但同時它自身還擁有特殊的屬性,并且與對象相比,函數還有一個特殊之處,就是它是可調用的,你可以手動調用函數去執(zhí)行某個操作?;谝陨咸厥馇闆r,在 ECMAScript 規(guī)范中規(guī)定了可以通過 typeof 區(qū)分出函數和其它對象。

除了上述能檢測出的七種類型之外,幾乎其它所有類型經 typeof 檢測后都是返回 object,例如:

console.log( typeof document.children );                 // object
console.log( typeof window );                            // object
console.log( typeof document.querySelector( "html" ) );  // object
console.log( typeof document.createElement( "div" ) );   // object
console.log( typeof new Map() );                         // object    
console.log( typeof new Set() );                         // object
console.log( typeof new Promise( () => {} ) );           // object

至此,可以得到一個初步結論,使用 typeof 運算符只能檢測出:字符串、數字、布爾值、函數、SymbolBigIntundefined 七種類型,對于數組、對象、null 和其它類型則無能為力,需要另尋他法。

這里還需要說明一個特殊情況,對于字符串、數字、布爾值這三種基本數據類型,還存在對應的特殊引用類型:

  • new String()
  • new Number()
  • new Boolean()
console.log( ( new String( "aa" ) ).valueOf() === "aa" );   // true
console.log( ( new Number( 1234 ) ).valueOf() === 1234 );   // true
console.log( ( new Boolean( true ) ).valueOf() === true );  // true

因此,一旦通過上述的方式創(chuàng)建字符串、數字或者布爾值,使用 typeof 將無法得到準確的類型:

console.log( typeof new String( "aa" ) );   // object
console.log( typeof new Number( 1234 ) );   // object
console.log( typeof new Boolean( true ) );  // object

由此可見,typeof 運算符對于字符串、數字和布爾值的類型判定,無法做到百分百的絕對精準。不過,在實際開發(fā)中,基本上極少會遇到使用上述特殊方式創(chuàng)建這三種數據類型的情況。因此,仍然可以繼續(xù)使用 typeof 進行判斷。

instanceof 運算符

以下是 MDN 關于 instanceof 的描述:

instanceof運算符用于檢測構造函數的 prototype 屬性是否出現在某個實例對象的原型鏈上。

語法:obj instanceof constructor

由于 instanceof 是基于 ”原型“ 的,因此它只適用于檢測引用數據類型,如:對象、數組等。

我們先來看一下示例:

const obj = {
    a: "a"
};
console.log( obj instanceof Object );  // true
console.log( Object.getPrototypeOf( obj ) === Object.prototype );  // true

在上面的示例中,obj 是一個通過字面量形式創(chuàng)建的對象,本質上相當于 new Object(),也就是說,obj 是由 Object() 構造函數構建出來的,那么 obj 的原型鏈上必然包含 Object 的原型。

再看一個數組的例子:

const arr = [ 1, 2, 3 ];
console.log( arr instanceof Array );  // true

同樣的原理,arr 是一個通過字面量形式創(chuàng)建的數組,本質上相當于 new Array(),那 arr 的原型鏈上也必然包含 Array 的原型,因此,上面的邏輯是沒問題的,但是如果對代碼稍加改造,將 Array 換成 Object 會是什么結果呢?

const arr = [ 1, 2, 3 ];
console.log( arr instanceof Object );  // true

結果顯示也為 true,這是因為在 JavaScript 中,數組其實也是對象,不僅僅是數組,凡是通過 new 關鍵字創(chuàng)建的實例本質上都是對象。所以,前文提到的 typeof new xxx 的結果都是 object。也正因如此,數組的原型鏈中也必然包含 Object 的原型。

另外需要說明的是,instanceof 在多 iframe 環(huán)境下會存在問題,因為這意味著存在多個全局環(huán)境,而不同的全局環(huán)境擁有不同的全局對象,從而擁有不同的內置類型構造函數,這將會導致 instanceof 出現混亂。

Object.prototype.toString.call()

這種絕妙的檢測方式最早是由 ”始祖級“ 的 JavaScript 類庫 Prototype.js 發(fā)掘出來的。這幾乎要追溯到近 20 年前了,那時的前端還處在萌芽時期,各種規(guī)范標準尚未完善,還要面對令人抓狂的瀏覽器兼容問題,因此要想準確檢測出各種數據類型簡直是難如登天。各大程序庫想盡了辦法,各種奇技淫巧層出不窮,直到這種方式的出現,終于有了一個穩(wěn)定的檢測方式,之后的庫和框架也基本都是用此方法來檢測數據類型。

它的根本原理實際上就是輸出對象內部的類屬性 [[Class]] 的值,這在絕大多數情況下是肯定準確的。這里先看第一個知識點:toString。

簡單來說,toString 方法就是將對象以字符串的形式返回。JavaScript 中幾乎所有對象都有 toString 方法,nullundefined 沒有 toString 方法,下面通過代碼示例看一下每種類型調用 toString 后返回的結果:

console.log( ( new String( "a" ) ).toString() );    // a
console.log( ( new Number( 100 ) ).toString() );    // 100
console.log( ( new Boolean( true ) ).toString() );  // true
console.log( [ 1,2,3 ].toString() );                // 1,2,3
console.log( { a: "a" }.toString() );               // [object Object]
console.log( Symbol().toString() );                 // Symbol()
console.log( 100n.toString() );                     // 100

上述結果可以看出,每個對象的 toString 方法都有自己的一套邏輯, 因此輸出的結果不盡相同,并且上面的結果也說明了,單純使用各自的 toString 方法得到的值也沒能表示出相關類型,只有一個 [object Object] 值得研究。

為什么會得到 [object Object] 呢?這是因為對象的 toString 方法無法將對象正確解析為字符串,所以 JavaScript 引擎直接返回了字符串 [object Object]。此時我們可以做出一個這樣的假設:因為是在 object 類型的數據上調用了 toString 方法,返回了 [object Object],而這個字符串中的兩個單詞都是 object(先不考慮大小寫),能否說明這個字符串實際已經包含了類型信息呢?如果這個假設成立,那么理論上其它類型的數據應該也可以通過這種方式獲取到類型。但是前面提到了,每個對象的 toString 方法都有自己的一套邏輯,返回的內容五花八門,現在就需要想辦法讓它們也能返回類似 [object Object] 這種形式的字符串,以此來推斷其所屬類型。這里就需要用到原型屬性,因為所有的對象都繼承自 Object,既然它們各自的 toString 方法有自己的邏輯,那我們就不用他們自身的 toString,而是使用繼承自 Object 原型上的 toString, 也就是 Object.prototype.toString,那為什么后面還用了一個 call 呢? 先來看一下不用 call 的結果:

console.log( Object.prototype.toString( [] ) );    // [object Object] 
console.log( Object.prototype.toString( {} ) );    // [object Object]
console.log( Object.prototype.toString( "aa" ) );  // [object Object]
console.log( Object.prototype.toString( 11 ) );    // [object Object]

單純使用 Object.prototype.toString 將一律返回 [object Object],因為這始終是在調用 ObjecttoString 方法,其內部的 this 始終指向的是 Object,所以就必須要借助 call 改變 this 的指向( apply 也可以 ), 所以才有了 Object.prototype.toString.call() 的寫法。其實可以這樣理解:我自己的 toString 被我重寫了,不能用了,那我就用 ObjecttoString,因為它是原始純凈的,能返回我想要的東西,并且我繼承自 Object,能借用它的一切,自然也就能借用它的 toString,只需在借用時注明是我在使用就可以了( call 的作用 )。

下面就看看使用 Object.prototype.toString.call() 到底能否返回我們想要的結果吧。

console.log( Object.prototype.toString.call( "aa" ) );            // [object String]
console.log( Object.prototype.toString.call( 1000 ) );            // [object Number]
console.log( Object.prototype.toString.call( true ) );            // [object Boolean]
console.log( Object.prototype.toString.call( 100n ) );            // [object BigInt]
console.log( Object.prototype.toString.call( null ) );            // [object Null]
console.log( Object.prototype.toString.call( undefined ) );       // [object Undefined]
console.log( Object.prototype.toString.call( Symbol() ) );        // [object Symbol]
console.log( Object.prototype.toString.call( [ 1,2,3 ] ) );       // [object Array]
console.log( Object.prototype.toString.call( { a: "a" } ) );      // [object Object]
console.log( Object.prototype.toString.call( function () {} ) );  // [object Function]

再看看其它類型的數據

// [object Promise]
console.log( Object.prototype.toString.call( new Promise( () => {} ) ) ); 

// [object HTMLHtmlElement]
console.log( Object.prototype.toString.call( document.querySelector( "html" ) ) );

// [object HTMLDivElement]
console.log( Object.prototype.toString.call( document.createElement( "div" ) ) );

// [object HTMLCollection]
console.log( Object.prototype.toString.call( document.children ) );

// [object HTMLDocument]
console.log( Object.prototype.toString.call( document ) );

// [object Window]
console.log( Object.prototype.toString.call( window ) );

// [object Set]
console.log( Object.prototype.toString.call( new Set() ) );

// [object Map]
console.log( Object.prototype.toString.call( new Map() ) );

根據以上結果可以得知,返回結果都是以 [object 開頭,以 類型] 結尾,那么我們加工一下就可以用它直接返回類型了:

Object.prototype.toString.call( obj ).slice( 8, -1 );

那這個方法真的絕對保險嗎?99% 的情況下是保險的,但不排除極特殊情況,比如:

Object.prototype.toString = () => "哈哈哈";

console.log( Object.prototype.toString.call( "aa" ) );  // 哈哈哈
console.log( Object.prototype.toString.call( 1000 ) );  // 哈哈哈
console.log( Object.prototype.toString.call( true ) );  // 哈哈哈
console.log( Object.prototype.toString.call( 100n ) );  // 哈哈哈

由此可見,如果最原始的 Object.prototype.toString 被改寫了,那么這個方法就失效了,不過正常情況下誰會這樣做呢?

封裝示例

基于以上各種檢測手段,我們可以封裝一個基本的類型檢測方法,下面是一個最基本的封裝示例,大家可以自行完善。

function getType ( data ) {
    
    // 對于簡單的類型直接使用 typeof 判斷
    let type = "";
    switch ( typeof data ) {
        case "string":    type === "string";    break;
        case "number":    type === "number";    break;
        case "boolean":   type === "boolean";   break;
        case "function":  type === "function";  break;
        case "symbol":    type === "symbol";    break;
        case "bigint":    type === "bigint";    break;
        case "undefined": type === "undefined"; break;
    }
    if ( type ) {
        return type;
    }

    // 數組類型直接使用原生提供的 Array.isArray
    if ( Array.isArray( data ) ) {
        return "array";
    }

    // 其余類型使用 Object.prototype.toString.call 獲取
    return Object.prototype.toString.call( data ).slice( 8, -1 ).toLowerCase();
}

以上就是JavaScript獲取數據類型的方法詳解的詳細內容,更多關于JavaScript獲取數據類型的資料請關注腳本之家其它相關文章!

相關文章

最新評論