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