關(guān)于JS數(shù)據(jù)類型檢測的多種方式總結(jié)
背景
總所周知,js是一門動態(tài)的弱類型腳本語言,其采用動態(tài)的類型系統(tǒng)以及基于原型的繼承方式。
缺乏類型的靜態(tài)約束,這意味著數(shù)據(jù)類型導(dǎo)致的程序錯誤并不能在編譯階段及時發(fā)現(xiàn),要想寫出健壯的代碼,就必須在運行時各種的check&兼容,所以能夠熟練準(zhǔn)確的檢測數(shù)據(jù)類型成為掌握這門語言最重要的基礎(chǔ)之一。
判斷數(shù)據(jù)類型的手段有哪些?
總的來說大致有以下幾種:typeof、instanceof、Object.prototype.toString、constructor、鴨式類型、及針對特定類型的檢測方法Array.isArray(),Number.isNaN(),雖然方法很多,但他們的使用場景有所不同。
1. 用typeof判斷基礎(chǔ)數(shù)據(jù)類型:
返回值有undefined、string、number、boolean、object、function、symbol七種。
可以看出,typeof作為官方提供的類型檢測操作符,在檢測undefined、string、boolean、symbol這些基本數(shù)據(jù)類型及function方面是十分靠譜的。表現(xiàn)拉垮的地方主要在于
1) 不能對具體對象類型(Array、Date、regExp)進行區(qū)分。
2) typeof null === 'object' // 竟然是true。。。。
缺陷 2)可以避免,在判斷對象引用類型時多判斷一句即可,typeof x === 'object' && x !== null。但是不能區(qū)分對象的具體類型,確實是個很大痛點。
2. 用instanceof判斷對象數(shù)據(jù)類型
此運算符用于檢測某個構(gòu)造函數(shù)的prototype是否出現(xiàn)在目標(biāo)對象的原型鏈上。
這是一種預(yù)測的檢測方式,并不會像typeof一樣直接將數(shù)據(jù)類型以字符串的方式進行返回,而是你需要預(yù)判對象類型的構(gòu)造函數(shù),最終返回一個boolean值。
檢測規(guī)則其實從命名就可以看出,判斷實例是否是由某個構(gòu)造函數(shù)所創(chuàng)建的,那么知道了原理,現(xiàn)在動手實現(xiàn)一個屬于自己的instanceof。
function myInstanceof(target,constructor){ const baseType = ['string', 'number','boolean','undefined','symbol'] if(baseType.includes(typeof(target))) { return false } //原型鏈其實就是個對象組成的鏈表,遍歷這個鏈表, let prototype = Object.getPrototypeOf(target); while(prototype){ //一旦鏈上有對象有符合,就返回true if(prototype === constructor.prototype){ return true }else{ prototype = Object.getPrototypeOf(prototype) } } return false } console.log(myInstanceof([],Array))
在js里,可以從廣義上認(rèn)為萬物源于對象,因為實例雖然是通過構(gòu)造函數(shù)創(chuàng)建的,但是構(gòu)造函數(shù)本身只是沒有感情的生產(chǎn)機器,實例的靈魂和性格(公共屬性和方法)都是共享自構(gòu)造函數(shù)的prototype屬性指向的那個原型對象,而且原型對象都是純對象,純對象又是由Object構(gòu)造函數(shù)創(chuàng)建的,那么就會造成下邊這種后果。
對于數(shù)組,遍歷原型鏈上的對象,Array.prototype Object.prototype都會出現(xiàn)。
并且,對字面量方式創(chuàng)建的基本數(shù)據(jù)類型無法進行判斷。比如
如何彌補上邊的缺陷呢,答案是可以在上邊特殊的場景中采用下邊的constructor代替instanceof。
3. 用contructor屬性
首先先明確。constructor是原型上的屬性,實例繼承自原型,所以實例上也能直接訪問此屬性。
首先看下contructor的通用性表現(xiàn)
意外的表現(xiàn)不錯,除了null、undefined,有contructor屬性的基礎(chǔ)(包裝)類型或者對象類型都能準(zhǔn)確判斷。
能準(zhǔn)確區(qū)分Array|Object 因為它沒有instanceof那樣會遍歷整條原型鏈,只是在實例身上進行判斷。但也有個致命的缺陷,實例上的這一屬性太容易被修改了,一旦修改,這個方法就沒有意義了。
4. toString方法
首先,js的對象類型或者基礎(chǔ)類型的包裝對象都有一個toString方法。繼承自O(shè)bject.prototype.toString(),調(diào)用會返回對應(yīng)類型的字符串標(biāo)記"[object Type]"。
這個方法有種亂拳打死老師傅,無心插柳柳成蔭的感覺,本來的作用只是得到一個表示該對象的字符串,現(xiàn)在用在js類型檢測上,表現(xiàn)簡直不要太好,針對基礎(chǔ)類型及對象類型表現(xiàn)都非常不錯,如果非要說個缺點,只能說返回的字符串有點復(fù)雜,使用不太方便,現(xiàn)在讓我們動手簡化一下。
先寫一個簡版
function isType(type,value){ return Object.prototype.toString.call(value) === `[object ${type}]` } console.log(isType('Array',[])) console.log(isType('Number',1))
這樣使用也不太方便,‘Array' ‘Number'這樣的類型參數(shù),很容易拼寫錯誤,所以希望方法可以預(yù)設(shè)參數(shù),并且希望構(gòu)造一個函數(shù)工廠,調(diào)用返回類似于isArray這樣的函數(shù)。在IDE中函數(shù)名相比字符串會擁有更好的代碼提示,不容易拼寫錯誤。
function isType(type){ return function(value){ return Object.prototype.toString.call(value) === `[object ${type}]` } } const isArray = isType('Array') const isNumber = isType('Number') console.log(isArray([]),isNumber(1))
這里運用了高階函數(shù)的思想,保留參數(shù)+返回一個新的函數(shù),那么可以想到j(luò)s里bind除了可以綁定this,也有保留參數(shù)+返回新函數(shù)的功能,用在這里也很合適。
function isType(type,value){ return Object.prototype.toString.call(value) === `[object ${type}]` } const isArray = isType.bind(null,'Array') const isNumber = isType.bind(null,'Number') console.log(isArray([]),isNumber(1))
更進一步,用參數(shù)柯里化的思想改造一波
function isType(type,value){ return Object.prototype.toString.call(value) === `[object ${type}]` } function curring (fn,...args1){ let len = fn.length; return function(...args2){ const args = args1.concat(args2); if(args.length < len){ return curring(fn,...args) }else{ return fn(...args) } } } const isArray = curring(isType,'Array') const isNumber = curring(isType,'Number') console.log(isArray([]),isNumber(1))
最后,豐富一下支持的類型,大功告成。
const types = [ 'Null', 'Undefined', 'String', 'Number', 'Boolean', 'Object', 'Array', 'Date', 'Function', 'RegExp', 'Symbol', 'Math', ] const checkTypeUtil = {} types.forEach((type)=>{ checkTypeUtil[`is${type}`] = curring(isType,type) }) export { checkTypeUtil } console.log(checkTypeUtil.isArray([]))
5. 用Array.isArray判斷數(shù)組
上邊提到 instanceof可以用來檢測數(shù)組,但是這在iframe創(chuàng)建的多window環(huán)境中,因為window全局環(huán)境需要隔離,所以Array和Array.prototype在每個窗口中必須是不同的,所以iframeA.Array.prototype ≠ iframeB.Array.prototype,所以 iframeA.arr instanceof iframeB.Array必定是返回false,這是小概率的事件,但是在使用iframe的場景里,互相傳值,也是非常可能發(fā)生的。使用ES6提供的Array.isArray就沒有這個問題,可以準(zhǔn)確判斷數(shù)組。
可以這樣 pollify
if (!Array.isArray) { Array.isArray = function(x) { return Object.prototype.toString.call(x) === '[object Array]'; }; }
6.區(qū)分ArrayLike與Array
類數(shù)組的定義是:
- 擁有l(wèi)ength屬性,其它屬性(索引)為非負整數(shù)(對象中的索引會被當(dāng)做字符串來處理
- 不具有數(shù)組所具有的方法
function isLikeArray(x){ if(!(typeof x === 'object' && x !== null)){ return false } return typeof x.length === 'number' && x.length >= 0 && !Array.isArray(x) }
類數(shù)組可以用Array.from Array.prototype.slice.call(val)來轉(zhuǎn)換為真正的數(shù)組。
7.判斷一個對象是否是純對象(or普通對象)
純對象的定義:特指通過一下三種方式創(chuàng)建的對象
- new Object
- 對象字面量創(chuàng)建 {}
- Object.create(null)
jquery、lodash源碼都是采用下邊的方法來檢測
const funcToString = Function.prototype.toString const objectCtorString = funcToString.call(Object) function isPlainObject(value){ // 先用toString先排除其他數(shù)據(jù)類型 if(!value || !Object.prototype.toString.call(value) === "[object Object]"){ return false } const proto = Object.getPrototypeOf(value) if(proto === null){//兼容Object.create(null)這樣創(chuàng)建的對象 return true } const Ctor = Object.prototype.hasOwnProperty.call(proto,'constructor') && proto.constructor; if(typeof Ctor !== 'function'){ return false } // 這里通過字符串判斷構(gòu)造函數(shù)是否是Object,而不是直接使用instanceof,是為了避免上邊提到的 多window環(huán)境Object不同的問題 if(funcToString.call(Ctor) === objectCtorString){ return true } return false } console.log(isPlainObject(Object.create(null))) console.log(isPlainObject(new Object)) console.log(isPlainObject({a:1}))
8. NaN如何檢測,Number.isNaN與isNaN有啥區(qū)別
結(jié)論:Number.isNaN會嚴(yán)格的判斷傳入的值是否是直接等于NaN。
isNaN則會先進行Number()轉(zhuǎn)換,然后再進行是否是NaN的判斷。
9. 鴨式類型檢測法
其實上邊利用constuctor判斷數(shù)據(jù)類型,就是采用了這種方法。判斷一個動物是不是鴨子,那么通過看起來像鴨子,叫起來像鴨子這樣簡單的經(jīng)驗判斷就可大致進行判斷。
比如判斷一個對象是不是一個Promise,就可以這樣
function isPromise(x){ if(!(x instanceof Promise)){ return false } return typeof x.then === 'function' }
總結(jié)
到此這篇關(guān)于JS數(shù)據(jù)類型檢測的文章就介紹到這了,更多相關(guān)JS數(shù)據(jù)類型檢測內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- JS中檢測數(shù)據(jù)類型的幾種方式及優(yōu)缺點小結(jié)
- 淺談javascript的數(shù)據(jù)類型檢測
- js數(shù)據(jù)類型檢測總結(jié)
- JavaScript中檢測數(shù)據(jù)類型的四種方法
- javascript基本數(shù)據(jù)類型及類型檢測常用方法小結(jié)
- 在javaScript中檢測數(shù)據(jù)類型的幾種方式小結(jié)
- JavaScript數(shù)據(jù)類型檢測代碼分享
- js學(xué)習(xí)總結(jié)_基于數(shù)據(jù)類型檢測的四種方式(必看)
- JS數(shù)組索引檢測中的數(shù)據(jù)類型問題詳解
- js中各種數(shù)據(jù)類型檢測和判定的實戰(zhàn)示例
相關(guān)文章
JavaScript的setAttribute兼容性問題解決方法
JavaScript的setAttribute存在兼容性問題,下面與大家分享下具體的解決方法,感興趣的朋友可以參考下2013-11-11老生常談JavaScript獲取CSS樣式的方法(兼容各瀏覽器)
大家都知道CSS樣式有三種類型:行內(nèi)樣式、內(nèi)部樣式和外部樣式,這篇文章主要介紹了javaScript獲取CSS樣式的方法(兼容各瀏覽器),需要的朋友可以參考下2018-09-09一款js和css代碼壓縮工具[附JAVA環(huán)境配置方法]
壓縮css和js是我們工作中經(jīng)常要處理的一件事,這里介紹的是一款基于YUICompressor,淘寶封裝的css和js壓縮工具TBCompressor.2010-04-04Javascript面試經(jīng)典套路reduce函數(shù)查重
reduce函數(shù),是ECMAScript5規(guī)范中出現(xiàn)的數(shù)組方法.下面通過本文給大家分享Javascript面試經(jīng)典套路reduce函數(shù)查重,需要的朋友參考下吧2017-03-03