JS類型判斷的四種方法詳解
引言
JavaScript中有七種原始數(shù)據(jù)類型和幾種引用數(shù)據(jù)類型,本文將清楚地介紹四種用于類型判斷的方法,分別是typeOf
、instanceOf
、Object.prototype.toString.call()
、Array.isArray()
,并介紹其使用方法和判定原理。
typeof
- 可以準(zhǔn)確判斷除
null
之外的所有原始類型,null
會(huì)被判定成object function
類型可以被準(zhǔn)確判斷為function
,而其他所有引用類型都會(huì)被判定為object
let s = '123' // string let n = 123 // number let f = true // boolean let u = undefined // undefined let nu = null // null let sy = Symbol(123) // Symbol let big = 1234n // BigInt let obj = {} let arr = [] let fn = function() {} let date = new Date() console.log(typeof s); // string typeof后面有無(wú)括號(hào)都行 console.log(typeof n); // number console.log(typeof f); // boolean console.log(typeof u); // undefined console.log(typeof(sy)); // symbol console.log(typeof(big)); // bigint console.log(typeof(nu)); // object console.log(typeof(obj)); // object console.log(typeof(arr)); // object console.log(typeof(date)); // object console.log(typeof(fn)); // function
判定原理
typeof是通過(guò)將值轉(zhuǎn)換為二進(jìn)制之后,判斷其前三位是否為0:都是0則為object,反之則為原始類型。因?yàn)樵碱愋娃D(zhuǎn)二進(jìn)制,前三位一定不都是0;反之引用類型被轉(zhuǎn)換成二進(jìn)制前三位一定都是0。
null
是原始類型卻被判定為object
就是因?yàn)樗跈C(jī)器中是用一長(zhǎng)串0來(lái)表示的,可以把這看作是一個(gè)史詩(shī)級(jí)的bug。
所以用typeof
判斷接收到的值是否為一個(gè)對(duì)象時(shí),還要注意排除null的情況:
function isObject() { if(typeof(o) === 'object' && o !== null){ return true } return false }
你丟一個(gè)值給typeof
,它會(huì)告訴你這個(gè)字值是什么類型,但是它無(wú)法準(zhǔn)確告訴你這是一個(gè)Array
或是Date
,若想要如此精確地知道一個(gè)對(duì)象類型,可以用instanceof
告訴你是否為某種特定的類型
instanceof
只能精確地判斷引用類型,不能判斷原始類型
console.log(obj instanceof Object);// true console.log(arr instanceof Array);// true console.log(fn instanceof Function);// true console.log(date instanceof Date);// true console.log(s instanceof String);// false console.log(n instanceof Number);// false console.log(arr instanceof Object);// true
判定原理
instanceof
既能把數(shù)組判定成Array
,又能把數(shù)組判定成Object
,究其原因是原型鏈的作用————順著數(shù)組實(shí)例 arr 的隱式原型一直找到了 Object 的構(gòu)造函數(shù),看下面的代碼:
arr.__proto__ = Array.prototype Array.prototype.__proto__ = Object.prototype
所以我們就知道了,instanceof
能準(zhǔn)確判斷出一個(gè)對(duì)象是否為某種類型,就是依靠對(duì)象的原型鏈來(lái)查找的,一層又一層地判斷直到找到null
為止。
手寫instanceOf
根據(jù)這個(gè)原理,我們可以手寫出一個(gè)instanceof
:
function myinstanceof(L, R) { while(L != null) { if(L.__proto__ === R.prototype){ return true; } L = L.__proto__; } return false; } console.log(myinstanceof([], Array)) // true console.log(myinstanceof({}, Object)) // true
Object.prototype.toString.call()
可以判斷任何數(shù)據(jù)類型
在瀏覽器上執(zhí)行這三段代碼,會(huì)得到'[object Object]'
,'[object Array]'
,'[object Number]'
var a = {} Object.prototype.toString.call(a) var a = {} Object.prototype.toString.call(a) var a = 123 Object.prototype.toString.call(a)
原型上的toString的內(nèi)部邏輯
調(diào)用Object.prototype.toString
的時(shí)候執(zhí)行會(huì)以下步驟:
- 如果此值是
undefined
類型,則返回‘[object Undefined]’
- 如果此值是
null
類型,則返回‘[object Null]’
- 將 O 作為
ToObject(this)
的執(zhí)行結(jié)果。toString
執(zhí)行過(guò)程中會(huì)調(diào)用一個(gè)ToObject
方法,執(zhí)行一個(gè)類似包裝類的過(guò)程,我們?cè)L問(wèn)不了這個(gè)方法,是JS自己用的 - 定義一個(gè)
class
作為內(nèi)部屬性[[class]]
的值。toString可以讀取到這個(gè)值并把這個(gè)值暴露出來(lái)讓我們看得見 - 返回由
"[object"
和class
和"]"
組成的字符串
為什么結(jié)合call就能準(zhǔn)確判斷值類型了呢?
① Object.prototype.toString(xxx)
往括號(hào)中不管傳遞什么返回結(jié)果都是'[object Object]'
,因?yàn)楦鶕?jù)上面五個(gè)步驟來(lái)看,它內(nèi)部會(huì)自動(dòng)執(zhí)行ToObject()
方法,xxx
會(huì)被執(zhí)行一個(gè)類似包裝類的過(guò)程然后轉(zhuǎn)變成一個(gè)對(duì)象。所以單獨(dú)一個(gè)Object.prototype.toString(xxx)
不能用來(lái)判定值的類型
② 首先了解call方法的核心原理就是:比如foo.call(obj)
,利用隱式綁定的規(guī)則,讓obj對(duì)象擁有foo這個(gè)函數(shù)的引用,從而讓foo函數(shù)的this指向obj,執(zhí)行完foo函數(shù)內(nèi)部邏輯后,再將foo函數(shù)的引用從obj上刪除掉。手搓一個(gè)call的源碼就是這樣的:
// call方法只允許被函數(shù)調(diào)用,所以它應(yīng)該是放在Function構(gòu)造函數(shù)的顯式原型上的 Function.prototype.mycall = function(context) { // 判斷調(diào)用我的那個(gè)哥們是不是函數(shù)體 if (typeof this !== 'function') { return new TypeError(this+ 'is not a function') } // this(函數(shù))里面的this => context對(duì)象 const fn = Symbol('key') // 定義一個(gè)獨(dú)一無(wú)二的fn,防止使用該源碼時(shí)與其他fn產(chǎn)生沖突 context[fn] = this // 讓對(duì)象擁有該函數(shù) context={Symbol('key'): foo} context[fn]() // 觸發(fā)隱式綁定 delete context[fn] }
③ 所以Object.prototype.toString.call(xxx)
就相當(dāng)于 xxx.toString()
,把toString()方法放在了xxx對(duì)象上調(diào)用,這樣就能精準(zhǔn)給出xxx的對(duì)象類型
toString方法有幾個(gè)版本:
{}.toString() 得到由"[object" 和 class 和 "]" 組成的字符串
[].toString() 數(shù)組的toString方法重寫了對(duì)象上的toString方法,返回由數(shù)組內(nèi)部元素以逗號(hào)拼接的字符串
xx.toString() 返回字符串字面量,比如
let fn = function(){}; console.log( fn.toString() ) // "function () {}"
Array.isArray(x)
只能判斷是否是數(shù)組,若傳進(jìn)去的x是數(shù)組,返回true,否則返回false
總結(jié)
typeOf:原始類型除了null都能準(zhǔn)確判斷,引用類型除了function能準(zhǔn)確判斷其他都不能。依靠值轉(zhuǎn)為二進(jìn)制后前三位是否為0來(lái)判斷
instanceOf:只能把引用類型丟給它準(zhǔn)確判斷。順著對(duì)象的隱式原型鏈向上比對(duì),與構(gòu)造函數(shù)的顯式原型相等返回true,否則false
Object.prototype.toString.call():可以準(zhǔn)確判斷任何類型。要了解對(duì)象原型的toString()內(nèi)部邏輯和call()的核心原理,二者結(jié)合才有精準(zhǔn)判定的效果
Array.isArray():是數(shù)組則返回true,不是則返回false。判定范圍最狹窄
以上就是JS類型判斷的四種方法詳解的詳細(xì)內(nèi)容,更多關(guān)于JS類型判斷的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript子窗口調(diào)用父窗口變量和函數(shù)的方法
這篇文章主要介紹了JavaScript子窗口調(diào)用父窗口變量和函數(shù)的方法,涉及JavaScript窗口調(diào)用的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Javascript this 的一些學(xué)習(xí)總結(jié)
相信有C++、C#或Java等編程經(jīng)驗(yàn)的各位,對(duì)于this關(guān)鍵字再熟悉不過(guò)了。由于Javascript是一種面向?qū)ο蟮木幊陶Z(yǔ)言,它和C++、C#或Java一樣都包含this關(guān)鍵字,接下來(lái)我們將向大家介紹Javascript中的this關(guān)鍵字2012-08-08原生js的ajax和解決跨域的jsonp(實(shí)例講解)
下面小編就為大家?guī)?lái)一篇原生js的ajax和解決跨域的jsonp(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10javascript觸發(fā)模擬鼠標(biāo)點(diǎn)擊事件
這篇文章主要介紹了javascript觸發(fā)模擬鼠標(biāo)點(diǎn)擊事件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-06-06深入理解requireJS-實(shí)現(xiàn)一個(gè)簡(jiǎn)單的模塊加載器
本篇文章主要介紹了深入理解requireJS-實(shí)現(xiàn)一個(gè)簡(jiǎn)單的模塊加載器,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01