js:typeof與instanceof區別
typeof
typeof運算子返回一個用來表示表示式的資料型別的字串。
返回值為6個字串,分別為string
、Boolean
、number
、function
、object
、undefined
。
let a = [1,2,3], b = 1, c = 'test', d = function(){console.log('test function')}, e = true, f = null, g; console.log(typeof(a)); //object console.log(typeof(b)); //number console.log(typeof(c)); //string console.log(typeof(d)); //function console.log(typeof(e)); //boolean console.log(typeof(f)); //object console.log(typeof(g)); //undefined
typeof的侷限性: typeof
在判斷null
、array
、object
以及函式例項(new + 函式)
時,得到的都是object,
這使得在判斷這些資料型別的時候,得不到真是的資料型別。
instanceof
instanceof用來檢測某個物件是不是另一個物件的例項。
官方的話:該運算髮用來測試一個物件在其原型鏈中是否存在一個建構函式prototype屬性
instance中文翻譯為例項,用來判斷該物件是誰的例項,同時我們也就知道instanceof是物件運算子。
這裡的例項就牽扯到了物件的繼承,它的判斷就是根據原型鏈進行搜尋,在物件obj1的原型鏈上如果存在另一個物件obj2的原型屬性,那麼表示式(obj1 instanceof obj2)返回值為true;否則返回false。var a = new Array(); console.log(a instanceof Array); //會返回 true console.log(a instanceof Object); // 也會返回 true 因為Array是object 的子類 -------------------------------------------------------------- function Foo(){} Foo.prototype = new Aoo(); // 原型繼承 var foo = new Foo(); console.log(foo instanceof Foo) //true console.log(foo instanceof Aoo) //true instanceof不僅可以判斷一層繼承關係,也可以判斷多層繼承關係 -------------------------------------------------------------- var a = new Array(); if(a instanceof Object) // 返回true if(window instanceof Object) // 返回false typeof(window) //會得到object 需要注意的是,如果表示式 obj instanceof Foo 返回true,則並不意味著該表示式會永遠返回ture,因為Foo.prototype屬性的值有可能會改變,改變之後的值很有可能不存在於obj的原型鏈上,這時原表示式的值就會成為false
總結:
typeof和instanceof都是用來判斷變數型別的,兩者的區別在於:
- typeof判斷所有變數的型別,返回值有number,boolean,string,function,object,undefined。
- typeof對於豐富的物件例項,只能返回"Object"字串。
- instanceof用來判斷物件,程式碼形式為obj1 instanceof obj2(obj1是否是obj2的例項),obj2必須為物件,否則會報錯!其返回值為布林值。
- instanceof可以對不同的物件例項進行判斷,判斷方法是根據物件的原型鏈依次向下查詢,如果obj2的原型屬性存在obj1的原型鏈上,(obj1 instanceof obj2)值為true。
補充:
使用 Object.prototype.tostring.call(obj) 檢測物件型別
先看使用示例
console.log(Object.prototype.toString.call("jerry"));//[object String] console.log(Object.prototype.toString.call(12));//[object Number] console.log(Object.prototype.toString.call(true));//[object Boolean] console.log(Object.prototype.toString.call(undefined));//[object Undefined] console.log(Object.prototype.toString.call(null));//[object Null] console.log(Object.prototype.toString.call({name: "jerry"}));//[object Object] console.log(Object.prototype.toString.call(function(){}));//[object Function] console.log(Object.prototype.toString.call([]));//[object Array] console.log(Object.prototype.toString.call(new Date));//[object Date] console.log(Object.prototype.toString.call(/\d/));//[object RegExp] function Person(){}; console.log(Object.prototype.toString.call(new Person));//[object Object]
為什麼這樣就能區分呢?於是我去看了一下toString方法的用法:toString方法返回反映這個物件的字串。
那為什麼不直接用obj.toString()呢?
console.log("jerry".toString());//jerry console.log((1).toString());//1 console.log([1,2].toString());//1,2 console.log(new Date().toString());//Wed Dec 21 2016 20:35:48 GMT+0800 (中國標準時間) console.log(function(){}.toString());//function (){} console.log(null.toString());//error console.log(undefined.toString());//error
同樣是檢測物件obj呼叫toString方法,obj.toString()的結果和Object.prototype.toString.call(obj)的結果不一樣,這是為什麼?
這是因為toString為Object的原型方法,而Array、Function等型別作為Object的例項,都重寫了toString方法。不同的物件型別呼叫toString方法時,根據原型鏈的知識,呼叫的是對應的重寫之後的toString方法(Function型別返回內容為函式體的字串,Array型別返回元素組成的字串.....),而不會去呼叫Object上原型toString方法(返回物件的具體型別),所以採用obj.toString()不能得到其物件型別,只能將obj轉換為字串型別;因此,在想要得到物件的具體型別時,應該呼叫Object上原型toString方法。
我們可以驗證一下,將陣列的toString方法刪除,看看會是什麼結果:
var arr=[1,2,3]; console.log(Array.prototype.hasOwnProperty("toString"));//true console.log(arr.toString());//1,2,3 delete Array.prototype.toString;//delete操作符可以刪除例項屬性 console.log(Array.prototype.hasOwnProperty("toString"));//false console.log(arr.toString());//"[object Array]"
刪除了Array的toString方法後,同樣再採用arr.toString()方法呼叫時,不再有遮蔽Object原型方法的例項方法,因此沿著原型鏈,arr最後呼叫了Object的toString方法,返回了和Object.prototype.toString.call(arr)相同的結果。