亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JavaScript獲取數(shù)據(jù)類型的方法詳解

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

說明

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

概述

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

數(shù)據(jù)類型種類

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

基本數(shù)據(jù)類型

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

引用數(shù)據(jù)類型

  • 對象( Object, Array 等等 )

區(qū)別

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

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

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

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

檢測方法

typeof 運算符

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

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

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

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

除了上述能檢測出的七種類型之外,幾乎其它所有類型經(jīng) 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

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

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

  • 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)建字符串、數(shù)字或者布爾值,使用 typeof 將無法得到準確的類型:

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

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

instanceof 運算符

以下是 MDN 關(guān)于 instanceof 的描述:

instanceof運算符用于檢測構(gòu)造函數(shù)的 prototype 屬性是否出現(xiàn)在某個實例對象的原型鏈上。

語法:obj instanceof constructor

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

我們先來看一下示例:

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

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

再看一個數(shù)組的例子:

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

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

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

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

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

Object.prototype.toString.call()

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

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

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

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

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

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

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

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

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]

再看看其它類型的數(shù)據(jù)

// [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() ) );

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

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;
    }

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

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

以上就是JavaScript獲取數(shù)據(jù)類型的方法詳解的詳細內(nèi)容,更多關(guān)于JavaScript獲取數(shù)據(jù)類型的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論