在 ECMAScript 規(guī)范中,共定義了 7 種數(shù)據(jù)類型赖阻,分為 基本類型 和 引用類型 兩大類框杜,如下所示:
基本類型:String浦楣、Number、Boolean霸琴、Symbol椒振、Undefined、Null?
引用類型:Object
基本類型也稱為簡單類型梧乘,由于其占據(jù)空間固定,是簡單的數(shù)據(jù)段庐杨,為了便于提升變量查詢速度选调,將其存儲在棧中,即按值訪問灵份。
引用類型也稱為復(fù)雜類型仁堪,由于其值的大小會改變,所以不能將其存放在棧中填渠,否則會降低變量查詢速度弦聂,因此,其值存儲在堆(heap)中氛什,而存儲在變量處的值莺葫,是一個指針,指向存儲對象的內(nèi)存處枪眉,即按址訪問捺檬。引用類型除 Object 外,還包括 Function 贸铜、Array堡纬、RegExp、Date 等等蒿秦。
鑒于 ECMAScript 是松散類型的烤镐,因此需要有一種手段來檢測給定變量的數(shù)據(jù)類型。對于這個問題棍鳖,JavaScript 也提供了多種方法炮叶,但遺憾的是,不同的方法得到的結(jié)果參差不齊。
下面介紹常用的4種方法悴灵,并對各個方法存在的問題進(jìn)行簡單的分析扛芽。
1、typeof
typeof 是一個操作符积瞒,其右側(cè)跟一個一元表達(dá)式川尖,并返回這個表達(dá)式的數(shù)據(jù)類型。返回的結(jié)果用該類型的字符串(全小寫字母)形式表示茫孔,包括以下 7 種:number叮喳、boolean、symbol缰贝、string馍悟、object、undefined剩晴、function 等锣咒。
typeof'';// string 有效
typeof1;// number 有效
typeofSymbol();// symbol 有效
typeoftrue;//boolean 有效
typeofundefined;//undefined 有效
typeofnull;//object 無效
typeof[] ;//object 無效
typeofnewFunction();// function 有效
typeofnewDate();//object 無效
typeofnewRegExp();//object 無效
有些時候,typeof 操作符會返回一些令人迷惑但技術(shù)上卻正確的值:
對于基本類型赞弥,除 null 以外毅整,均可以返回正確的結(jié)果。
對于引用類型绽左,除 function 以外悼嫉,一律返回 object 類型。
對于 null 拼窥,返回 object 類型戏蔑。
對于 function 返回 ?function 類型。
其中鲁纠,null 有屬于自己的數(shù)據(jù)類型 Null 总棵, 引用類型中的 數(shù)組、日期房交、正則 也都有屬于自己的具體類型彻舰,而 typeof 對于這些類型的處理,只返回了處于其原型鏈最頂端的 Object 類型候味,沒有錯刃唤,但不是我們想要的結(jié)果。
2白群、instanceof
instanceof 是用來判斷 A 是否為 B 的實例尚胞,表達(dá)式為:A instanceof B,如果 A 是 B 的實例帜慢,則返回 true,否則返回 false笼裳。 在這里需要特別注意的是:instanceof 檢測的是原型唯卖,我們用一段偽代碼來模擬其內(nèi)部執(zhí)行過程:
instanceof (A,B) = {
????varL = A.__proto__;
????varR = B.prototype;
????if(L === R) {
????????// A的內(nèi)部屬性 __proto__ 指向 B 的原型對象
????????returntrue;
????}
????returnfalse;
}
從上述過程可以看出,當(dāng) A 的 __proto__ 指向 B 的 prototype 時躬柬,就認(rèn)為 A 就是 B 的實例拜轨,我們再來看幾個例子:
[] instanceof Array;// true
{} instanceof Object;// true
newDate() instanceof Date;// true
function Person(){};
newPerson() instanceof Person;
[] instanceof Object;// true
newDate() instanceof Object;// true
newPerson instanceof Object;// true
我們發(fā)現(xiàn),雖然 instanceof 能夠判斷出 [ ] 是Array的實例允青,但它認(rèn)為 [ ] 也是Object的實例橄碾,為什么呢?
我們來分析一下 [ ]颠锉、Array法牲、Object 三者之間的關(guān)系:
從 instanceof 能夠判斷出 [ ].__proto__ ?指向 Array.prototype,而 Array.prototype.__proto__ 又指向了Object.prototype琼掠,最終 Object.prototype.__proto__ 指向了null拒垃,標(biāo)志著原型鏈的結(jié)束。因此瓷蛙,[]悼瓮、Array、Object 就在內(nèi)部形成了一條原型鏈:
從原型鏈可以看出速挑,[] 的 __proto__ ?直接指向Array.prototype谤牡,間接指向 Object.prototype,所以按照?instanceof?的判斷規(guī)則姥宝,[] 就是Object的實例。依次類推恐疲,類似的 new Date()腊满、new Person() 也會形成一條對應(yīng)的原型鏈 。因此培己,instanceof 只能用來判斷兩個對象是否屬于實例關(guān)系碳蛋, 而不能判斷一個對象實例具體屬于哪種類型。
instanceof?操作符的問題在于省咨,它假定只有一個全局執(zhí)行環(huán)境肃弟。如果網(wǎng)頁中包含多個框架,那實際上就存在兩個以上不同的全局執(zhí)行環(huán)境零蓉,從而存在兩個以上不同版本的構(gòu)造函數(shù)笤受。如果你從一個框架向另一個框架傳入一個數(shù)組,那么傳入的數(shù)組與在第二個框架中原生創(chuàng)建的數(shù)組分別具有各自不同的構(gòu)造函數(shù)敌蜂。
variframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[0].Array;
vararr =newxArray(1,2,3);// [1,2,3]
arr instanceof Array;// false
針對數(shù)組的這個問題箩兽,ES5提供了?Array.isArray()方法 。該方法用以確認(rèn)某個對象本身是否為 Array 類型章喉,而不區(qū)分該對象在哪個環(huán)境中創(chuàng)建汗贫。
if(Array.isArray(value)){
???//對數(shù)組執(zhí)行某些操作
}
Array.isArray() 本質(zhì)上檢測的是對象的 [[Class]] 值身坐,[[Class]] 是對象的一個內(nèi)部屬性,里面包含了對象的類型信息落包,其格式為 [object Xxx] 部蛇,Xxx 就是對應(yīng)的具體類型 。對于數(shù)組而言咐蝇,[[Class]] 的值就是 [object Array] 涯鲁。
3、constructor
當(dāng)一個函數(shù) F被定義時嘹害,JS引擎會為F添加 prototype 原型撮竿,然后再在 prototype上添加一個 constructor 屬性,并讓其指向 F 的引用笔呀。如下所示:
當(dāng)執(zhí)行 var f = new F() 時幢踏,F(xiàn) 被當(dāng)成了構(gòu)造函數(shù),f 是F的實例對象许师,此時 F 原型上的 constructor 傳遞到了 f 上房蝉,因此 f.constructor == F
可以看出,F(xiàn) 利用原型對象上的 constructor 引用了自身微渠,當(dāng) F 作為構(gòu)造函數(shù)來創(chuàng)建對象時搭幻,原型上的 constructor 就被遺傳到了新創(chuàng)建的對象上, 從原型鏈角度講逞盆,構(gòu)造函數(shù) F 就是新對象的類型檀蹋。這樣做的意義是,讓新對象在誕生以后云芦,就具有可追溯的數(shù)據(jù)類型俯逾。
同樣,JavaScript 中的內(nèi)置對象在內(nèi)部構(gòu)建時也是這樣做的:
細(xì)節(jié)問題:
1. null 和 undefined 是無效的對象舅逸,因此是不會有 constructor 存在的桌肴,這兩種類型的數(shù)據(jù)需要通過其他方式來判斷。
2. 函數(shù)的 constructor 是不穩(wěn)定的琉历,這個主要體現(xiàn)在自定義對象上坠七,當(dāng)開發(fā)者重寫 prototype 后,原有的 constructor 引用會丟失旗笔,constructor 會默認(rèn)為 Object
為什么變成了 Object彪置?
因為 prototype 被重新賦值的是一個 { }, { } 是 new Object() 的字面量换团,因此 new Object() 會將 Object 原型上的 constructor 傳遞給 { }悉稠,也就是 Object 本身。
因此艘包,為了規(guī)范開發(fā)的猛,在重寫對象原型時一般都需要重新給 constructor 賦值耀盗,以保證對象實例的類型不被篡改。
4卦尊、toString
toString() 是 Object 的原型方法叛拷,調(diào)用該方法,默認(rèn)返回當(dāng)前對象的 [[Class]] 岂却。這是一個內(nèi)部屬性忿薇,其格式為 [object Xxx] ,其中 Xxx 就是對象的類型躏哩。
對于 Object 對象署浩,直接調(diào)用 toString()? 就能返回 [object Object] 。而對于其他對象扫尺,則需要通過 call / apply 來調(diào)用才能返回正確的類型信息筋栋。
Object.prototype.toString.call('') ;??// [object String]
Object.prototype.toString.call(1) ;???// [object Number]
Object.prototype.toString.call(true) ;// [object Boolean]
Object.prototype.toString.call(Symbol());//[object Symbol]
Object.prototype.toString.call(undefined) ;// [object Undefined]
Object.prototype.toString.call(null) ;// [object Null]
Object.prototype.toString.call(newFunction()) ;// [object Function]
Object.prototype.toString.call(newDate()) ;// [object Date]
Object.prototype.toString.call([]) ;// [object Array]
Object.prototype.toString.call(newRegExp()) ;// [object RegExp]
Object.prototype.toString.call(newError()) ;// [object Error]
Object.prototype.toString.call(document) ;// [object HTMLDocument]
Object.prototype.toString.call(window) ;//[object global] window 是全局對象 global 的引用