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

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

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

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

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

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

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

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

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

    • Array: 特殊類型的對象,用于存儲有序的元素集合。

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

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

  • Symbol

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

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

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

  • BigInt

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

特點

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

示例

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

// 運算
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'

二、類型判斷時會產(chǎ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

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

為什么對null的類型判斷為object?

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

為什么單獨function判斷類型為function,而非object?

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

2. instanceof類型判斷

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

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

如果object是通過Constructor或其任意父類(通過原型鏈)構(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,因為所有對象都最終繼承自O(shè)bject

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

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

例如:

let str = "some text";

console.log(str instanceof String); // 可能意外地輸出: false,因為字符串不是String對象的實例
// 實際上,"some text" 在進(jìn)行 instanceof 檢查前會被轉(zhuǎn)換為 String("some text"),但這是臨時的包裝對象,檢查后即被銷毀。

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')

面試題補充:請寫出instanceof的判斷原理。

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

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

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

3. Object.prototype.toString.call( )

Object.prototype.toString.call() 是JavaScript中一個強(qiáng)大的方法,用于獲取任何值的類型信息。這個方法能夠返回一個表示該值的字符串,這個字符串格式通常為"[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]"
......
......
......

在面試時,很多大廠面試官會為難你,問:有什么辦法可以判斷類型?

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

他又問你為什么這個方法可以判斷類型?

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

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

  • Object.prototype.toString

先來看官方文檔上寫的Object.prototype.toString

翻譯:

調(diào)用tostring方法時,會執(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值,該值是由三個String“[object”、class“]” 連接起來的結(jié)果。

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

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

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

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

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

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

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

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

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

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

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

  • call

不了解call的,我簡單舉個例子來說明一下效果:

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

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

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

var object = { 
    a: 11
}; 

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

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

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

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

具體來說:

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

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

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

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

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

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

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

4.Array.isArray(x)

Array.isArray(x) 是JavaScript的一個內(nèi)建函數(shù),用于檢測x是否為一個數(shù)組。這個方法提供了最直接和可靠的方式來判斷一個變量是否是數(shù)組類型,相比使用instanceoftypeof等方法更準(zhǔn)確,因為它不會受到不同全局執(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

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

三、結(jié)語:

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

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

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

相關(guān)文章

最新評論