JS中獲取數(shù)據(jù)類型的方法,你真的知道嗎?不一定吧!
今天我們來討論一下js中獲取數(shù)據(jù)類型的各種方法。
在討論這個話題之前,我們先來看看js中的數(shù)據(jù)類型。在最新的 ECMAScript 標(biāo)準(zhǔn)中定義了 8 種數(shù)據(jù)類型:Boolean,Null,Undefined,Number,BigInt,String,Symbol 和 對象類型。
Boolean表示一個邏輯實體,可以有兩個值:true 和 false。Null 類型只有一個值: null。
Undefined 類型一個沒有被賦值的變量會有個默認(rèn)值 undefined。
數(shù)字類型,根據(jù) ECMAScript 標(biāo)準(zhǔn),JavaScript 中只有一種數(shù)字類型:基于 IEEE 754 標(biāo)準(zhǔn)的雙精度 64 位二進(jìn)制格式的值(-(253 -1) 到 253 -1)。除了能夠表示浮點數(shù)外,還有一些帶符號的值:+Infinity,-Infinity 和 NaN 。
BigInt 類型是 JavaScript 中的一個基礎(chǔ)的數(shù)值類型,可以用任意精度表示整數(shù)。使用 BigInt,您可以安全地存儲和操作大整數(shù),甚至可以超過數(shù)字的安全整數(shù)限制。BigInt是通過在整數(shù)末尾附加 n 或調(diào)用構(gòu)造函數(shù)來創(chuàng)建的。
String類型用于表示文本數(shù)據(jù)。它是一組16位的無符號整數(shù)值的“元素”。在字符串中的每個元素占據(jù)了字符串的位置。第一個元素的索引為0,下一個是索引1,依此類推。字符串的長度是它的元素的數(shù)量。
JavaScript 字符串是不可更改的。這意味著字符串一旦被創(chuàng)建,就不能被修改。但是,可以基于對原始字符串的操作來創(chuàng)建新的字符串。
符號(Symbols)是ECMAScript 第6版新定義的。符號類型是唯一的并且是不可修改的, 并且也可以用來作為Object的key的值(如下). 在某些語言當(dāng)中也有類似的原子類型(Atoms). 你也可以認(rèn)為為它們是C里面的枚舉類型。
對象類型,在 Javascript 里,對象可以被看作是一組屬性的集合。申明的方式一般是 var obj = new Object()。
下面我們來正式看看js中四種判斷js數(shù)據(jù)類型的方法。
1、typeof
typeof 返回一個表示數(shù)據(jù)類型的字符串,返回結(jié)果包括:number、boolean、string、object、undefined、function等6種數(shù)據(jù)類型。看下面的例子:

typeof 可以對JS基礎(chǔ)數(shù)據(jù)類型做出準(zhǔn)確的判斷,而對于引用類型返回的基本上都是object, 其實返回object也沒有錯,因為所有對象的原型鏈最終都指向了Object,Object是所有對象的`祖宗`。 但當(dāng)我們需要知道某個對象的具體類型時,typeof 就顯得有些力不從心了。
2、instanceof
instanceof 是用來判斷 A 是否為 B 的實例對,表達(dá)式為:A instanceof B,如果A是B的實例,則返回true,否則返回false。 在這里需要特別注意的是:instanceof檢測的是原型。
我們再來看幾個例子:

我們發(fā)現(xiàn),雖然 instanceof 能夠判斷出 [] 是Array的實例,但它認(rèn)為 [] 也是Object的實例,為什么呢? 我們來分析一下[]、Array、Object 三者之間的關(guān)系: 從instanceof 能夠判斷出 [].__proto__ 指向 Array.prototype, 而 Array.prototype.__proto__ 又指向了Object.prototype,Object.prototype.__proto__ 指向了null,標(biāo)志著原型鏈的結(jié)束。因此,[]、Array、Object就形成了如下圖所示的一條原型鏈:

從原型鏈可以看出,[] 的 __proto__ 直接指向Array.prototype, 間接指向Object.prototype, 所以按照 instanceof 的判斷規(guī)則,[] 就是Object的實例。當(dāng)然,類似的new Date()、new Person() 也會形成這樣一條原型鏈,因此,instanceof 只能用來判斷兩個對象是否屬于原型鏈的關(guān)系, 而不能獲取對象的具體類型。
3、constructor
當(dāng)一個函數(shù)F被定義時,JS引擎會為F添加prototype原型,然后再在prototype上添加一個constructor屬性,并讓其指向F的引用。如下所示:

當(dāng)執(zhí)行 var f = new F() 時,F(xiàn)被當(dāng)成了構(gòu)造函數(shù),f是F的實例對象,此時F原型上的constructor傳遞到了f上,因此f.constructor == F

可以看出,JS在函數(shù)F的原型上定義了constructor,當(dāng)F被當(dāng)作構(gòu)造函數(shù)用來創(chuàng)建對象時,創(chuàng)建的新對象就被標(biāo)記為了“F” 類型,使得新對象有名有姓,可以追溯。同理,JS中的數(shù)據(jù)類型也遵守這個規(guī)則:

細(xì)節(jié)問題:
null和undefined是無效的對象,因此是不會有constructor存在的,這兩種類型的數(shù)據(jù)需要通過typeof來判斷。
JS對象的constructor是不穩(wěn)定的,這個主要體現(xiàn)在自定義對象上,當(dāng)開發(fā)者重寫prototype后,原有的constructor會丟失,constructor會默認(rèn)為Object

為什么變成了Object?prototype被重新賦值的是一個{}, {}是new Object()的字面量,因此new Object()會將Object原型上的constructor傳遞給{},也就是Object本身。因此,為了規(guī)范,在重寫對象原型時一般都需要重新給constructor賦值,以保證實例對象的類型不被改寫。
4、Object.prototype.toString
toString是Object原型對象上的一個方法,該方法默認(rèn)返回其調(diào)用者的具體類型,更嚴(yán)格的講,是 toString運(yùn)行時this指向的對象類型, 返回的類型格式為[object,xxx],xxx是具體的數(shù)據(jù)類型,其中包括:String,Number,Boolean,Undefined,Null,Function,Date,Array,RegExp,Error,HTMLDocument,... 基本上所有對象的類型都可以通過這個方法獲取到。

需要注意的是,必須通過Object.prototype.toString.call來獲取,而不能直接 new Date().toString(), 從原型鏈的角度講,所有對象的原型鏈最終都指向了Object, 按照J(rèn)S變量查找規(guī)則,其他對象應(yīng)該也可以直接訪問到Object的toString方法,而事實上,大部分的對象都實現(xiàn)了自身的toString方法,這樣就可能會導(dǎo)致Object的toString被終止查找,因此要用call來強(qiáng)制執(zhí)行Object的toString方法。