編寫(xiě)javascript代碼的時(shí)候常常要判斷變量,字面量的類(lèi)型,可以用typeof,instanceof,Array.isArray(),等方法,究竟哪一種最方便,最實(shí)用,最省心呢?本文探討這個(gè)問(wèn)題。
成都創(chuàng)新互聯(lián)是創(chuàng)新、創(chuàng)意、研發(fā)型一體的綜合型網(wǎng)站建設(shè)公司,自成立以來(lái)公司不斷探索創(chuàng)新,始終堅(jiān)持為客戶(hù)提供滿(mǎn)意周到的服務(wù),在本地打下了良好的口碑,在過(guò)去的10余年時(shí)間我們累計(jì)服務(wù)了上千家以及全國(guó)政企客戶(hù),如護(hù)欄打樁機(jī)等企業(yè)單位,完善的項(xiàng)目管理流程,嚴(yán)格把控項(xiàng)目進(jìn)度與質(zhì)量監(jiān)控加上過(guò)硬的技術(shù)實(shí)力獲得客戶(hù)的一致稱(chēng)譽(yù)。1. typeof
1.1 語(yǔ)法
typeof返回一個(gè)字符串,表示未經(jīng)計(jì)算的操作數(shù)的類(lèi)型。
語(yǔ)法:typeof(operand) | typeof operand
參數(shù):一個(gè)表示對(duì)象或原始值的表達(dá)式,其類(lèi)型將被返回
描述:typeof可能返回的值如下:
類(lèi)型 結(jié)果
Undefined“undefined” Null“object” Boolean“boolean” Number“number” Bigint“bigint” String“string” Symbol“symbol”
宿主對(duì)象(由JS環(huán)境提供) 取決于具體實(shí)現(xiàn)
Function對(duì)象 “function”
其他任何對(duì)象 “object”
從定義和描述上來(lái)看,這個(gè)語(yǔ)法可以判斷出很多的數(shù)據(jù)類(lèi)型,但是仔細(xì)觀察,typeof null居然返回的是“object”,讓人摸不著頭腦,下面會(huì)具體介紹,先看看這個(gè)效果:
// 數(shù)值 console.log(typeof 37) // number console.log(typeof 3.14) // number console.log(typeof(42)) // number console.log(typeof Math.LN2) // number console.log(typeof Infinity) // number console.log(typeof NaN) // number 盡管它是Not-A-Number的縮寫(xiě),實(shí)際NaN是數(shù)字計(jì)算得到的結(jié)果,或者將其他類(lèi)型變量轉(zhuǎn)化成數(shù)字失敗的結(jié)果 console.log(Number(1)) //number Number(1)構(gòu)造函數(shù)會(huì)把參數(shù)解析成字面量 console.log(typeof 42n) //bigint // 字符串 console.log(typeof '') //string console.log(typeof 'boo') // string console.log(typeof `template literal`) // string console.log(typeof '1') //string 內(nèi)容為數(shù)字的字符串仍然是字符串 console.log(typeof(typeof 1)) //string,typeof總是返回一個(gè)字符串 console.log(typeof String(1)) //string String將任意值轉(zhuǎn)換成字符串 // 布爾值 console.log(typeof true) // boolean console.log(typeof false) // boolean console.log(typeof Boolean(1)) // boolean Boolean會(huì)基于參數(shù)是真值還是虛值進(jìn)行轉(zhuǎn)換 console.log(typeof !!(1)) // boolean 兩次調(diào)用!!操作想短語(yǔ)Boolean() // Undefined console.log(typeof undefined) // undefined console.log(typeof declaredButUndefinedVariabl) // 未賦值的變量返回undefined console.log(typeof undeclaredVariable ) // 未定義的變量返回undefined // 對(duì)象 console.log(typeof {a: 1}) //object console.log(typeof new Date()) //object console.log(typeof /s/) // 正則表達(dá)式返回object // 下面的例子令人迷惑,非常危險(xiǎn),沒(méi)有用處,應(yīng)避免使用,new操作符返回的實(shí)例都是對(duì)象 console.log(typeof new Boolean(true)) // object console.log(typeof new Number(1)) // object console.log(typeof new String('abc')) // object // 函數(shù) console.log(typeof function () {}) // function console.log(typeof class C { }) // function console.log(typeof Math.sin) // function
1.2 迷之null
javascript誕生以來(lái),typeof null都是返回‘object’的,這個(gè)是因?yàn)閖avascript中的值由兩部分組成,一部分是表示類(lèi)型的標(biāo)簽,另一部分是表示實(shí)際的值。對(duì)象類(lèi)型的值類(lèi)型標(biāo)簽是0,不巧的是null表示空指針,它的類(lèi)型標(biāo)簽也被設(shè)計(jì)成0,于是就有這個(gè)typeof null === ‘object’這個(gè)‘惡魔之子’。
曾經(jīng)有ECMAScript提案讓typeof null返回‘null’,但是該提案被拒絕了。
1.3 使用new操作符
除Function之外所有構(gòu)造函數(shù)的類(lèi)型都是‘object’,如下:
var str = new String('String'); var num = new Number(100) console.log(typeof str) // object console.log(typeof num) // object var func = new Function() console.log(typeof func) // function
1.4 語(yǔ)法中的括號(hào)
typeof運(yùn)算的優(yōu)先級(jí)要高于“+”操作,但是低于圓括號(hào)
var iData = 99 console.log(typeof iData + ' Wisen') // number Wisen console.log(typeof (iData + 'Wisen')) // string
1.5 判斷正則表達(dá)式的兼容性問(wèn)題
typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1 typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1
1.6 錯(cuò)誤
ECMAScript 2015之前,typeof總能保證對(duì)任何所給的操作數(shù)都返回一個(gè)字符串,即使是沒(méi)有聲明,沒(méi)有賦值的標(biāo)示符,typeof也能返回undefined,也就是說(shuō)使用typeof永遠(yuǎn)不會(huì)報(bào)錯(cuò)。
但是ES6中加入了塊級(jí)作用域以及l(fā)et,const命令之后,在變量聲明之前使用由let,const聲明的變量都會(huì)拋出一個(gè)ReferenceError錯(cuò)誤,塊級(jí)作用域變量在塊的頭部到聲明變量之間是“暫時(shí)性死區(qū)”,在這期間訪問(wèn)變量會(huì)拋出錯(cuò)誤。如下:
console.log(typeof undeclaredVariable) // 'undefined' console.log(typeof newLetVariable) // ReferenceError console.log(typeof newConstVariable) // ReferenceError console.log(typeof newClass) // ReferenceError let newLetVariable const newConstVariable = 'hello' class newClass{}
1.7 例外
當(dāng)前所有瀏覽器都暴露一個(gè)類(lèi)型為undefined的非標(biāo)準(zhǔn)宿主對(duì)象document.all。typeof document.all === 'undefined'。景觀規(guī)范允許為非標(biāo)準(zhǔn)的外來(lái)對(duì)象自定義類(lèi)型標(biāo)簽,單要求這些類(lèi)型標(biāo)簽與已有的不同,document.all的類(lèi)型標(biāo)簽為undefined的例子在web領(lǐng)域被歸類(lèi)為對(duì)原ECMA javascript標(biāo)準(zhǔn)的“故意侵犯”,可能就是瀏覽器的惡作劇。
總結(jié):typeof返回變量或者值的類(lèi)型標(biāo)簽,雖然對(duì)大部分類(lèi)型都能返回正確結(jié)果,但是對(duì)null,構(gòu)造函數(shù)實(shí)例,正則表達(dá)式這三種不太理想。
2. instanceof
2.1 語(yǔ)法
instanceof運(yùn)算符用于檢測(cè)實(shí)例對(duì)象(參數(shù))的原型鏈上是否出現(xiàn)構(gòu)造函數(shù)的prototype。
語(yǔ)法:object instanceof constructor
參數(shù):object 某個(gè)實(shí)例對(duì)象
constructor 某個(gè)構(gòu)造函數(shù)
描述:instanceof運(yùn)算符用來(lái)檢測(cè)constructor.property是否存在于參數(shù)object的原型鏈上。
// 定義構(gòu)造函數(shù) function C() { } function D() { } var o = new C() console.log(o instanceof C) //true,因?yàn)镺bject.getPrototypeOf(0) === C.prototype console.log(o instanceof D) //false,D.prototype不在o的原型鏈上 console.log(o instanceof Object) //true 同上 C.prototype = {} var o2 = new C() console.log(o2 instanceof C) // true console.log(o instanceof C) // false C.prototype指向了一個(gè)空對(duì)象,這個(gè)空對(duì)象不在o的原型鏈上 D.prototype = new C() // 繼承 var o3 = new D() console.log(o3 instanceof D) // true console.log(o3 instanceof C) // true C.prototype現(xiàn)在在o3的原型鏈上
需要注意的是,如果表達(dá)式obj instanceof Foo返回true,則并不意味著該表達(dá)式會(huì)永遠(yuǎn)返回true,應(yīng)為Foo.prototype屬性的值可能被修改,修改之后的值可能不在obj的原型鏈上,這時(shí)表達(dá)式的值就是false了。另外一種情況,改變obj的原型鏈的情況,雖然在當(dāng)前ES規(guī)范中,只能讀取對(duì)象的原型而不能修改它,但是借助非標(biāo)準(zhǔn)的__proto__偽屬性,是可以修改的,比如執(zhí)行obj.__proto__ = {}后,obj instanceof Foo就返回false了。此外ES6中Object.setPrototypeOf(),Reflect.setPrototypeOf()都可以修改對(duì)象的原型。
instanceof和多全局對(duì)象(多個(gè)iframe或多個(gè)window之間的交互)
瀏覽器中,javascript腳本可能需要在多個(gè)窗口之間交互。多個(gè)窗口意味著多個(gè)全局環(huán)境,不同全局環(huán)境擁有不同的全局對(duì)象,從而擁有不同的內(nèi)置構(gòu)造函數(shù)。這可能會(huì)引發(fā)一些問(wèn)題。例如表達(dá)式[] instanceof window.frames[0].Array會(huì)返回false,因?yàn)?/p>
Array.prototype !== window.frames[0].Array.prototype。
起初,這樣可能沒(méi)有意義,但是當(dāng)在腳本中處理多個(gè)frame或多個(gè)window以及通過(guò)函數(shù)將對(duì)象從一個(gè)窗口傳遞到另一個(gè)窗口時(shí),這就是一個(gè)非常有意義的話題。實(shí)際上,可以通過(guò)Array.isArray(myObj)或者Object.prototype.toString.call(myObj) = "[object Array]"來(lái)安全的檢測(cè)傳過(guò)來(lái)的對(duì)象是否是一個(gè)數(shù)組。
2.2 示例
String對(duì)象和Date對(duì)象都屬于Object類(lèi)型(它們都由Object派生出來(lái))。
但是,使用對(duì)象文字符號(hào)創(chuàng)建的對(duì)象在這里是一個(gè)例外,雖然原型未定義,但是instanceof of Object返回true。
var simpleStr = "This is a simple string"; var myString = new String(); var newStr = new String("String created with constructor"); var myDate = new Date(); var myObj = {}; var myNonObj = Object.create(null); console.log(simpleStr instanceof String); // 返回 false,雖然String.prototype在simpleStr的原型鏈上,但是后者是字面量,不是對(duì)象 console.log(myString instanceof String); // 返回 true console.log(newStr instanceof String); // 返回 true console.log(myString instanceof Object); // 返回 true console.log(myObj instanceof Object); // 返回 true, 盡管原型沒(méi)有定義 console.log(({}) instanceof Object); // 返回 true, 同上 console.log(myNonObj instanceof Object); // 返回 false, 一種創(chuàng)建非 Object 實(shí)例的對(duì)象的方法 console.log(myString instanceof Date); //返回 false console.log( myDate instanceof Date); // 返回 true console.log(myDate instanceof Object); // 返回 true console.log(myDate instanceof String); // 返回 false
注意:instanceof運(yùn)算符的左邊必須是一個(gè)對(duì)象,像"string" instanceof String,true instanceof Boolean這樣的字面量都會(huì)返回false。
下面代碼創(chuàng)建了一個(gè)類(lèi)型Car,以及該類(lèi)型的對(duì)象實(shí)例mycar,instanceof運(yùn)算符表明了這個(gè)myca對(duì)象既屬于Car類(lèi)型,又屬于Object類(lèi)型。
function Car(make, model, year) { this.make = make; this.model = model; this.year = year; } var mycar = new Car("Honda", "Accord", 1998); var a = mycar instanceof Car; // 返回 true var b = mycar instanceof Object; // 返回 true
不是...的實(shí)例
要檢測(cè)對(duì)象不是某個(gè)構(gòu)造函數(shù)的實(shí)例時(shí),可以使用!運(yùn)算符,例如if(!(mycar instanceof Car))
instanceof雖然能夠判斷出對(duì)象的類(lèi)型,但是必須要求這個(gè)參數(shù)是一個(gè)對(duì)象,簡(jiǎn)單類(lèi)型的變量,字面量就不行了,很顯然,這在實(shí)際編碼中也是不夠?qū)嵱谩?/p>
總結(jié):obj instanceof constructor雖然能判斷出對(duì)象的原型鏈上是否有構(gòu)造函數(shù)的原型,但是只能判斷出對(duì)象類(lèi)型變量,字面量是判斷不出的。
3. Object.prototype.toString()
3.1. 語(yǔ)法
toString()方法返回一個(gè)表示該對(duì)象的字符串。
語(yǔ)法:obj.toString()
返回值:一個(gè)表示該對(duì)象的字符串
描述:每個(gè)對(duì)象都有一個(gè)toString()方法,該對(duì)象被表示為一個(gè)文本字符串時(shí),或一個(gè)對(duì)象以預(yù)期的字符串方式引用時(shí)自動(dòng)調(diào)用。默認(rèn)情況下,toString()方法被每個(gè)Object對(duì)象繼承,如果此方法在自定義對(duì)象中未被覆蓋,toString()返回“[object type]”,其中type是對(duì)象的類(lèi)型,看下面代碼:
var o = new Object(); console.log(o.toString()); // returns [object Object]
注意:如ECMAScript 5和隨后的Errata中所定義,從javascript1.8.5開(kāi)始,toString()調(diào)用null返回[object, Null],undefined返回[object Undefined]
3.2. 示例
覆蓋默認(rèn)的toString()方法
可以自定義一個(gè)方法,來(lái)覆蓋默認(rèn)的toString()方法,該toString()方法不能傳入?yún)?shù),并且必須返回一個(gè)字符串,自定義的toString()方法可以是任何我們需要的值,但如果帶有相關(guān)的信息,將變得非常有用。
下面代碼中定義Dog對(duì)象類(lèi)型,并在構(gòu)造函數(shù)原型上覆蓋toString()方法,返回一個(gè)有實(shí)際意義的字符串,描述當(dāng)前dog的姓名,顏色,性別,飼養(yǎng)員等信息。
function Dog(name,breed,color,sex) { this.name = name; this.breed = breed; this.color = color; this.sex = sex; } Dog.prototype.toString = function dogToString() { return "Dog " + this.name + " is a " + this.sex + " " + this.color + " " + this.breed } var theDog = new Dog("Gabby", "Lab", "chocolate", "female"); console.log(theDog.toString()) //Dog Gabby is a female chocolate Lab
4. 使用toString()檢測(cè)數(shù)據(jù)類(lèi)型
目前來(lái)看toString()方法能夠基本滿(mǎn)足javascript數(shù)據(jù)類(lèi)型的檢測(cè)需求,可以通過(guò)toString()來(lái)檢測(cè)每個(gè)對(duì)象的類(lèi)型。為了每個(gè)對(duì)象都能通過(guò)Object.prototype.toString()來(lái)檢測(cè),需要以Function.prototype.call()或者Function.prototype.apply()的形式來(lái)檢測(cè),傳入要檢測(cè)的對(duì)象或變量作為第一個(gè)參數(shù),返回一個(gè)字符串"[object type]"。
// null undefined console.log(Object.prototype.toString.call(null)) //[object Null] 很給力 console.log(Object.prototype.toString.call(undefined)) //[object Undefined] 很給力 // Number console.log(Object.prototype.toString.call(Infinity)) //[object Number] console.log(Object.prototype.toString.call(Number.MAX_SAFE_INTEGER)) //[object Number] console.log(Object.prototype.toString.call(NaN)) //[object Number],NaN一般是數(shù)字運(yùn)算得到的結(jié)果,返回Number還算可以接受 console.log(Object.prototype.toString.call(1)) //[object Number] var n = 100 console.log(Object.prototype.toString.call(n)) //[object Number] console.log(Object.prototype.toString.call(0)) // [object Number] console.log(Object.prototype.toString.call(Number(1))) //[object Number] 很給力 console.log(Object.prototype.toString.call(new Number(1))) //[object Number] 很給力 console.log(Object.prototype.toString.call('1')) //[object String] console.log(Object.prototype.toString.call(new String('2'))) // [object String] // Boolean console.log(Object.prototype.toString.call(true)) // [object Boolean] console.log(Object.prototype.toString.call(new Boolean(1))) //[object Boolean] // Array console.log(Object.prototype.toString.call(new Array(1))) // [object Array] console.log(Object.prototype.toString.call([])) // [object Array] // Object console.log(Object.prototype.toString.call(new Object())) // [object Object] function foo() {} let a = new foo() console.log(Object.prototype.toString.call(a)) // [object Object] // Function console.log(Object.prototype.toString.call(Math.floor)) //[object Function] console.log(Object.prototype.toString.call(foo)) //[object Function] // Symbol console.log(Object.prototype.toString.call(Symbol('222'))) //[object Symbol] // RegExp console.log(Object.prototype.toString.call(/sss/)) //[object RegExp]
上面的結(jié)果,除了NaN返回Number稍微有點(diǎn)差池之外其他的都返回了意料之中的結(jié)果,都能滿(mǎn)足實(shí)際開(kāi)發(fā)的需求,于是我們可以寫(xiě)一個(gè)通用的函數(shù)來(lái)檢測(cè)變量,字面量的類(lèi)型。如下:
let Type = (function () { let type = {}; let typeArr = ['String', 'Object', 'Number', 'Array', 'Undefined', 'Function', 'Null', 'Symbol', 'Boolean', 'RegExp', 'BigInt']; for (let i = 0; i < typeArr.length; i++) { (function (name) { type['is' + name] = function (obj) { return Object.prototype.toString.call(obj) === '[object ' + name + ']' } })(typeArr[i]) } return type })() let s = true console.log(Type.isBoolean(s)) // true console.log(Type.isRegExp(/22/)) // true
除了能檢測(cè)ECMAScript規(guī)定的八種數(shù)據(jù)類(lèi)型(七種原始類(lèi)型,Boolean,Null,Undefined,Number,BigInt,String,Symbol,一種復(fù)合類(lèi)型Object)之外,還能檢測(cè)出正則表達(dá)式RegExp,F(xiàn)unction這兩種類(lèi)型,基本上能滿(mǎn)足開(kāi)發(fā)中的判斷數(shù)據(jù)類(lèi)型需求。
5. 判斷相等
既然說(shuō)道這里,不妨說(shuō)一說(shuō)另一個(gè)開(kāi)發(fā)中常見(jiàn)的問(wèn)題,判斷一個(gè)變量是否等于一個(gè)值。ES5中比較兩個(gè)值是否相等,可以使用相等運(yùn)算符(==),嚴(yán)格相等運(yùn)算符(===),但它們都有缺點(diǎn),== 會(huì)將‘4’轉(zhuǎn)換成4,后者NaN不等于自身,以及+0 !=== -0。ES6中提出”Same-value equality“(同值相等)算法,用來(lái)解決這個(gè)問(wèn)題。Object.is就是部署這個(gè)算法的新方法,它用來(lái)比較兩個(gè)值是否嚴(yán)格相等,與嚴(yán)格比較運(yùn)算(===)行為基本一致。
console.log(5 == '5') // true console.log(NaN == NaN) // false console.log(+0 == -0) // true console.log({} == {}) // false console.log(5 === '5') // false console.log(NaN === NaN) // false console.log(+0 === -0) // true console.log({} === {}) // false
Object.js()不同之處有兩處,一是+0不等于-0,而是NaN等于自身,如下:
let a = {} let b = {} let c = b console.log(a === b) // false console.log(b === c) // true console.log(Object.is(b, c)) // true
注意兩個(gè)空對(duì)象不能判斷相等,除非是將一個(gè)對(duì)象賦值給另外一個(gè)變量,對(duì)象類(lèi)型的變量是一個(gè)指針,比較的也是這個(gè)指針,而不是對(duì)象內(nèi)部屬性,對(duì)象原型等。
以上就是javascript中判斷數(shù)據(jù)類(lèi)型的幾種方式的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注創(chuàng)新互聯(lián)其它相關(guān)文章!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。