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

深入探究JavaScript的類型判斷(從基礎(chǔ)到精通)

 更新時(shí)間:2024年05月20日 10:13:34   作者:純粹要努力  
JavaScript 語(yǔ)言具有多種數(shù)據(jù)類型,它們可以大致分為兩大類:基本數(shù)據(jù)類型(Primitive Data Types)和引用數(shù)據(jù)類型(Reference Data Types),本文將帶大家一起從基礎(chǔ)到精通深入探究JavaScript的類型判斷,需要的朋友可以參考下

JavaScript 語(yǔ)言具有多種數(shù)據(jù)類型,它們可以大致分為兩大類:基本數(shù)據(jù)類型(Primitive Data Types)和引用數(shù)據(jù)類型(Reference Data Types)。

一、數(shù)據(jù)類型

基本數(shù)據(jù)類型(Primitive Data Types) 包括:

  • Undefined: 表示變量已被聲明但未被初始化時(shí)的值。
  • Null: 代表一個(gè)刻意的空值或缺失的值。
  • Boolean: 只有兩個(gè)值,true 或 false。
  • Number: 用于表示整數(shù)和浮點(diǎn)數(shù),包括Infinity、-Infinity和NaN。
  • String: 用于表示文本,由零個(gè)或多個(gè)字符組成。
  • Symbol: ES6 引入的新類型,表示獨(dú)一無(wú)二的、不可變的數(shù)據(jù)類型,主要用于對(duì)象的屬性鍵。
  • BigInt: ES10 引入,用于表示任意大小的整數(shù)。

引用數(shù)據(jù)類型(Reference Data Types) 包括:

  • Object: 一種復(fù)雜數(shù)據(jù)結(jié)構(gòu),可以包含多個(gè)鍵值對(duì),包括但不限于普通對(duì)象、數(shù)組、函數(shù)等。

    • Array: 特殊類型的對(duì)象,用于存儲(chǔ)有序的元素集合。

    • Function: 在JavaScript中,函數(shù)也是對(duì)象,可以作為值傳遞,擁有方法和屬性

上面的數(shù)據(jù)類型如果說(shuō)有不熟悉的,那一般是SymbolBigInt,下面我簡(jiǎn)要說(shuō)一下:

  • Symbol

最重要的特征就是唯一性,例如:

let a = Symbol(1) 
let b = Symbol(1) 
console.log(a === b)  // false

Symbol() 返回的東西,具有唯一性。

  • BigInt

Number 類型的安全整數(shù)范圍(-2^53 到 2^53),超出這個(gè)范圍的數(shù)進(jìn)行計(jì)算會(huì)出現(xiàn)精度丟失的問(wèn)題,算不準(zhǔn)確,于是就出現(xiàn)了BigInt.

特點(diǎn)

  • 創(chuàng)建: BigInt 可以通過(guò)在整數(shù)末尾添加 n 來(lái)創(chuàng)建,例如 123n。你也可以使用 BigInt() 函數(shù)將字符串轉(zhuǎn)換為 BigInt,如 BigInt("123")
  • 運(yùn)算: BigInt 和 Number 類型在進(jìn)行算術(shù)運(yùn)算時(shí)需要特別注意類型匹配。兩個(gè) BigInt 類型可以直接進(jìn)行加減乘除等運(yùn)算,但 BigInt 和 Number 直接運(yùn)算會(huì)導(dǎo)致錯(cuò)誤,需要先將 Number 轉(zhuǎn)換為 BigInt。
  • 比較: BigInt 和 Number 之間可以進(jìn)行寬松的相等性比較(==),但嚴(yán)格相等性比較(===)會(huì)因?yàn)轭愋筒煌祷?false。嚴(yán)格比較時(shí),需要確保類型一致。
  • 不支持: BigInt 不支持一元運(yùn)算符 ++--,也不適用于Math對(duì)象的方法,以及不能用于某些JavaScript原生對(duì)象的屬性,比如數(shù)組的長(zhǎng)度。
  • 字符串轉(zhuǎn)換: BigInt 轉(zhuǎn)換為字符串時(shí),會(huì)保持其完整的數(shù)值,不會(huì)發(fā)生精度丟失。

示例

// 創(chuàng)建 BigInt
const largeNum = 1234567890123456789012345678901234567890n;

// 運(yùn)算
const anotherLargeNum = 9876543210987654321098765432109876543210n;
const sum = largeNum + anotherLargeNum;

// 比較
console.log(largeNum === BigInt('1234567890123456789012345678901234567890')); // true
console.log(largeNum == 1234567890123456789012345678901234567890); // true, 松散比較
console.log(largeNum === 1234567890123456789012345678901234567890); // false, 嚴(yán)格比較類型不同

// 字符串轉(zhuǎn)換
console.log(largeNum.toString()); // '1234567890123456789012345678901234567890'

二、類型判斷時(shí)會(huì)產(chǎn)生的疑問(wèn)

1. typeof()類型判斷

console.log(typeof (null));//object

console.log(typeof (undefined));//undefined
console.log(typeof (true));//boolean
console.log(typeof (20));//number
console.log(typeof ("abc"));//string
console.log(typeof (Symbol()));//symbol
console.log(typeof (34n));//bigint

console.log(typeof ([]));//object
console.log(typeof ({}));//object

console.log(typeof (function () { }));//function

我們看上面的代碼會(huì)產(chǎn)生兩個(gè)疑問(wèn):

為什么對(duì)null的類型判斷為object?

這個(gè)行為實(shí)際上是JavaScript設(shè)計(jì)初期的一個(gè)決策,后來(lái)成為了語(yǔ)言的一部分,被視為一個(gè)歷史遺留問(wèn)題。在JavaScript的最初設(shè)計(jì)中,類型信息是通過(guò)值的內(nèi)部表示來(lái)區(qū)分的,特別是通過(guò)值的頭部比特位。對(duì)于當(dāng)時(shí)的實(shí)現(xiàn)來(lái)說(shuō),null的內(nèi)部二進(jìn)制表示是全零,這與對(duì)象類型在內(nèi)存中的某些標(biāo)記模式相吻合(判斷其二進(jìn)制前三位是否為0,是則為object,否則為原始類型),尤其是當(dāng)引擎檢查值的頭部比特以快速區(qū)分基本類型和引用類型時(shí),全零可能被錯(cuò)誤地解釋為了一個(gè)空對(duì)象的標(biāo)記。至于后來(lái)為什么不改,則是因?yàn)榇罅康钠髽I(yè)已經(jīng)用JavaScript寫(xiě)了大量的項(xiàng)目,改動(dòng)后,全部項(xiàng)目都會(huì)報(bào)錯(cuò),基于這種考慮就沒(méi)動(dòng)。因此在以后判斷類型是否為object時(shí),需要將null排除。

為什么單獨(dú)function判斷類型為function,而非object?

在JavaScript中,函數(shù)(Function)是一種特殊的對(duì)象,這意味著它本質(zhì)上繼承了對(duì)象的特性,可以擁有屬性和方法。然而,出于對(duì)語(yǔ)言設(shè)計(jì)和實(shí)用性考慮,typeof操作符特意將函數(shù)類型區(qū)分對(duì)待,當(dāng)應(yīng)用于函數(shù)時(shí),它返回的是"function"而不是"object"。

2. instanceof類型判斷

在JavaScript中,instanceof 是一個(gè)操作符,用于檢測(cè)構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在某個(gè)實(shí)例對(duì)象的原型鏈上。它的使用方式與Java中的instanceof相似,但概念上更符合JavaScript的原型繼承模型。其基本語(yǔ)法如下:

object instanceof Constructor
  • object:需要檢查的對(duì)象。
  • Constructor:一個(gè)構(gòu)造函數(shù)或者函數(shù)對(duì)象。

如果object是通過(guò)Constructor或其任意父類(通過(guò)原型鏈)構(gòu)造的,那么instanceof操作符返回true;否則,返回false

例如:

function Animal() {}
function Dog() {}
Dog.prototype = new Animal();

let myDog = new Dog();

console.log(myDog instanceof Dog); // 輸出: true
console.log(myDog instanceof Animal); // 輸出: true
console.log(myDog instanceof Object); // 輸出: true,因?yàn)樗袑?duì)象都最終繼承自O(shè)bject

在這個(gè)例子中,myDog對(duì)象是通過(guò)Dog構(gòu)造函數(shù)創(chuàng)建的,而Dog的原型鏈上包含了Animal,因此myDog既是Dog的實(shí)例,也是Animal的實(shí)例。同時(shí),由于JavaScript中所有對(duì)象都繼承自Object,所以myDog也是Object的實(shí)例。

原始類型(如stringnumber、boolean、null、undefined、symbol、bigint)不是對(duì)象,因此不能直接使用instanceof來(lái)判斷這些類型。如果你嘗試對(duì)原始類型值使用instanceof,它們會(huì)被臨時(shí)轉(zhuǎn)換為對(duì)應(yīng)的包裝對(duì)象(如new String()、new Number()、new Boolean()),然后再進(jìn)行檢查,但這通常不是你想要的行為,并且對(duì)于nullundefined這樣的值,這樣做會(huì)直接導(dǎo)致錯(cuò)誤。

例如:

let str = "some text";

console.log(str instanceof String); // 可能意外地輸出: false,因?yàn)樽址皇荢tring對(duì)象的實(shí)例
// 實(shí)際上,"some text" 在進(jìn)行 instanceof 檢查前會(huì)被轉(zhuǎn)換為 String("some text"),但這是臨時(shí)的包裝對(duì)象,檢查后即被銷毀。

let num = 2;

console.log(num instanceof Number); // 同樣可能輸出: false

let bool = true;

console.log(bool instanceof Boolean); // 輸出: false

console.log(null instanceof Object); // 拋出 TypeError: null is not an object (evaluating 'null instanceof Object')
console.log(undefined instanceof Object); // 拋出 TypeError: undefined is not an object (evaluating 'undefined instanceof Object')

面試題補(bǔ)充:請(qǐng)寫(xiě)出instanceof的判斷原理。

如果面試官出這種題,那對(duì)你算是非常溫柔了。

function myinstanceof(object, constructor) { 
    // 當(dāng)對(duì)象不為null時(shí)進(jìn)入循環(huán),因?yàn)閚ull沒(méi)有__proto__
    while (object !== null) {
        // 如果對(duì)象的原型等于構(gòu)造函數(shù)的prototype屬性,說(shuō)明該對(duì)象是構(gòu)造函數(shù)的實(shí)例
        if (object.__proto__ === constructor.prototype) { 
            return true; 
        } else { 
            // 如果當(dāng)前對(duì)象不是實(shí)例,繼續(xù)向上查找其原型鏈
            object = object.__proto__; 
        }
    } 
    // 遍歷完原型鏈都沒(méi)有找到匹配,說(shuō)明不是該構(gòu)造函數(shù)的實(shí)例
    return false; 
}

console.log(myinstanceof({}, Object));  // true
console.log(myinstanceof({}, Array));  // false

3. Object.prototype.toString.call( )

Object.prototype.toString.call() 是JavaScript中一個(gè)強(qiáng)大的方法,用于獲取任何值的類型信息。這個(gè)方法能夠返回一個(gè)表示該值的字符串,這個(gè)字符串格式通常為"[object Type]",其中Type是JavaScript中的類型名稱。相比于前兩種判斷存在的瑕疵和不準(zhǔn)確,這種更為完美和準(zhǔn)確。

console.log(Object.prototype.toString.call({}));      // "[object Object]"
console.log(Object.prototype.toString.call([]));     // "[object Array]"
console.log(Object.prototype.toString.call(new Date)); // "[object Date]"
console.log(Object.prototype.toString.call(null));    // "[object Null]"
console.log(Object.prototype.toString.call(undefined)); // "[object Undefined]"
......
......
......

在面試時(shí),很多大廠面試官會(huì)為難你,問(wèn):有什么辦法可以判斷類型?

你回答:Object.prototype.toString.call( ),

他又問(wèn)你為什么這個(gè)方法可以判斷類型?

要回答好這個(gè)問(wèn)題,就需要我們對(duì)Object.prototype.toString.call( )有著更為深入的理解:

這個(gè)方法要分兩部分理解:Object.prototype.toStringcall()

  • Object.prototype.toString

先來(lái)看官方文檔上寫(xiě)的Object.prototype.toString

翻譯:

調(diào)用tostring方法時(shí),會(huì)執(zhí)行以下步驟:

1.如果該值未定義,則返回“[object Undefined]”

2.如果this值為null,則返回“[object Null]”

3.讓o成為調(diào)用To Obiect的結(jié)果,將this值作為參數(shù)傳遞。(將 o 作為 To Object(this) 的執(zhí)行結(jié)果)

4.定義classo的內(nèi)部屬性 [[Class]] 的值

5.返回String值,該值是由三個(gè)String“[object”、class“]” 連接起來(lái)的結(jié)果。

你可以將以上步驟理解為以下步驟:

1.檢查值是否為undefined:如果調(diào)用toString的方法的對(duì)象是undefined,則返回"[object Undefined]"

2.檢查值是否為null:如果該值是null,則返回"[object Null]"。

3.轉(zhuǎn)換為對(duì)象(To Object操作):對(duì)于非null和非undefined的值,首先通過(guò)抽象操作To Object將其轉(zhuǎn)換為對(duì)象(如果還不是對(duì)象)。這意味著原始值(如數(shù)字、字符串等)會(huì)先轉(zhuǎn)換為它們的包裝對(duì)象,然后繼續(xù)后續(xù)步驟。

4.獲取內(nèi)部屬性[[Class]]:獲取轉(zhuǎn)換后的對(duì)象的內(nèi)部屬性[[Class]]的值。這個(gè)屬性由JavaScript引擎維護(hù),代表了對(duì)象的類型信息,比如"Array""Date"、"Object"等。

5.構(gòu)造并返回結(jié)果字符串:最后,將字符串"[object "class的值,以及"]"拼接起來(lái),形成并返回最終的類型字符串,如"[object Array]""[object Date]"等。

然而用Object.prototype.toString()遠(yuǎn)遠(yuǎn)不足以有效的判斷類型,盡管它很強(qiáng)大:

Object.prototype.toString()判斷類型時(shí),通常不會(huì)按照預(yù)期工作,尤其是當(dāng)直接在原始值(如字符串、數(shù)字、布爾)上嘗試時(shí),因?yàn)檫@樣調(diào)用的this并沒(méi)有綁定到你想要檢查的對(duì)象上。

其原因在于第三個(gè)步驟To Obiect,官方文檔對(duì)此如下描述:

它會(huì)new一個(gè)對(duì)象,而不是單純的字面量,此時(shí)其this指向發(fā)生改變,其內(nèi)部屬性[[class]]為指向?qū)ο蟮膬?nèi)部屬性object。

因此,需要call()的幫助改變其this指向

  • call

不了解call的,我簡(jiǎn)單舉個(gè)例子來(lái)說(shuō)明一下效果:

var object = { 
    a: 11
} 
function foo() { 
    console.log(this.a) 
} 
foo.call(obj)  // 11

想通過(guò)func函數(shù)輸出1,可以通過(guò)call方法將func里的this指向object。

我們可以嘗試模擬call方法的行為,寫(xiě)如下代碼:

var object = { 
    a: 11
}; 

function foo() { 
    console.log(this.a); 
} 

Function.prototype.mycall = function(context) {
    // 檢查調(diào)用mycall的是否為一個(gè)函數(shù)
    if (typeof this !== 'function') {
        throw new TypeError(this + ' is not a function');
    }
    
    // 使用Symbol來(lái)避免屬性名沖突
    const fn = Symbol('key');
    
    // 將當(dāng)前函數(shù)(this指向的func)賦值給context的一個(gè)唯一屬性
    context[fn] = this;
    
    // 調(diào)用這個(gè)新添加的函數(shù),此時(shí)this會(huì)被隱式綁定到context上
    context[fn]();
    
    // 刪除臨時(shí)添加的屬性,以清理環(huán)境
    delete context[fn];
};

foo.mycall(object);  // 輸出: 11

核心原理可以概括為: call方法通過(guò)在指定的context對(duì)象上臨時(shí)引用并調(diào)用目標(biāo)函數(shù),實(shí)現(xiàn)了對(duì)該函數(shù)內(nèi)部this的隱式綁定,從而使得函數(shù)能夠在預(yù)期的上下文中執(zhí)行。

具體來(lái)說(shuō):

  • 臨時(shí)綁定:它本質(zhì)上是在context對(duì)象上創(chuàng)建一個(gè)屬性(通常使用一個(gè)不易沖突的屬性名,如使用Symbol),并將目標(biāo)函數(shù)賦值給這個(gè)屬性。(Symbol作用在于,防止別人調(diào)用你寫(xiě)的方法時(shí),用同名的變量名)
  • 調(diào)用函數(shù):接著,通過(guò)context上的這個(gè)屬性間接調(diào)用目標(biāo)函數(shù)。由于是通過(guò)對(duì)象屬性的方式來(lái)調(diào)用的,JavaScript的函數(shù)調(diào)用規(guī)則決定了此時(shí)函數(shù)內(nèi)的this將綁定到該對(duì)象(即context)上。
  • 清理:為了不污染context對(duì)象,調(diào)用結(jié)束后通常還會(huì)刪除之前添加的臨時(shí)屬性,即清除本來(lái)就不存在context里的屬性。

看完這兩部分的解釋,再來(lái)做一個(gè)總結(jié):

使用Object.prototype.toString.call()時(shí),當(dāng)參數(shù)為Boolean,NumberString類型,call()先將這個(gè)原始值轉(zhuǎn)換為其對(duì)應(yīng)的包裝對(duì)象,即new String('11'),然后再調(diào)用Object.prototype.toString方法,當(dāng)執(zhí)行到第三步to object時(shí),發(fā)現(xiàn)參數(shù)為對(duì)象,則將對(duì)象賦給變量o。在這個(gè)過(guò)程中this指向因?yàn)?strong>call的糾正作用沒(méi)有發(fā)生改變,因此,其內(nèi)部屬性[[class]]沒(méi)有發(fā)生改變。

加上了call,你可以理解為以下情況:

Object.prototype.toString.call('11'); // 輸出: "[object String]" 
Object.prototype.toString.call(new String('11')); // 輸出同樣為: "[object String]"

寫(xiě)出完整步驟: 當(dāng)執(zhí)行Object.prototype.toString.call('11')時(shí),其內(nèi)部過(guò)程大致如下:

  • 字符串字面量'11'作為call的第一個(gè)參數(shù),使得toString方法內(nèi)部的this指向了一個(gè)臨時(shí)創(chuàng)建的String對(duì)象(即new String('1'))。
  • 該方法檢查this不是nullundefined,繼續(xù)執(zhí)行。
  • 將這個(gè)臨時(shí)的字符串對(duì)象視為操作對(duì)象O。
  • O中獲取其內(nèi)部屬性[[Class]],得到值"String"。
  • 組合并返回字符串"[object String]",表示這是一個(gè)字符串類型的對(duì)象。

4.Array.isArray(x)

Array.isArray(x) 是JavaScript的一個(gè)內(nèi)建函數(shù),用于檢測(cè)x是否為一個(gè)數(shù)組。這個(gè)方法提供了最直接和可靠的方式來(lái)判斷一個(gè)變量是否是數(shù)組類型,相比使用instanceoftypeof等方法更準(zhǔn)確,因?yàn)樗粫?huì)受到不同全局執(zhí)行環(huán)境(如iframe、Web Workers)中Array構(gòu)造函數(shù)不同的影響。

使用示例:

let arr = [1, 2, 3];
let notArr = "I am not an array";

console.log(Array.isArray(arr)); // 輸出: true
console.log(Array.isArray(notArr)); // 輸出: false

這個(gè)函數(shù)非常有用,尤其是在處理可能是多種類型輸入的動(dòng)態(tài)數(shù)據(jù)時(shí),能夠確保你正確地識(shí)別并處理數(shù)組類型的數(shù)據(jù)。

三、結(jié)語(yǔ):

在JavaScript的世界里,準(zhǔn)確無(wú)誤地判斷數(shù)據(jù)類型是編寫(xiě)健壯、可維護(hù)代碼的基礎(chǔ)。本文從基礎(chǔ)出發(fā),系統(tǒng)梳理了JavaScript的各大數(shù)據(jù)類型,重點(diǎn)解析了在類型判斷時(shí)常見(jiàn)的疑惑與誤區(qū),尤其深入探討了typeof、instanceof以及Object.prototype.toString.call()這三種類型判斷方法的原理與實(shí)踐,最后還提到了Array.isArray()這一專門用于數(shù)組類型判斷的便捷工具。

通過(guò)本篇內(nèi)容的學(xué)習(xí),你不僅掌握了每種判斷方法的適用場(chǎng)景與限制,還理解了如何利用Object.prototype.toString.call()這一終極武器來(lái)實(shí)現(xiàn)精確無(wú)誤的類型識(shí)別。記住,每種方法都有其獨(dú)特的價(jià)值和潛在的陷阱,合理選擇才能在實(shí)戰(zhàn)中游刃有余。

以上就是深入探究JavaScript的類型判斷(從基礎(chǔ)到精通)的詳細(xì)內(nèi)容,更多關(guān)于JavaScript類型判斷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論