天天看點

《JavaScript架構設計》——1.4 類型的判定

本節書摘來自異步社群《javascript架構設計》一書中的第1章,第1.4節,作者:司徒正美著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

javascript存在兩套類型系統,一套是基本資料類型,另一套是對象類型系統。基本資料類型包括6種,分别是undefined、string、null、boolean、function、object。基本資料類型是通過typeof來檢測的。對象類型系統是以基礎類型系統為基礎的,通過instanceof來檢測。然而,javascript自帶的這兩套識别機制非常不靠譜,于是催生了isxxx系列。就拿typeof來說,它隻能粗略識别出 string、number、boolean、function、undefined、object這6種資料類型,無法識别null、regexparagument 等細分對象類型。

讓我們看一下這裡面究竟有多少陷阱。

上面分4組,第一組是typeof的坑。第二組是instanceof 的陷阱,隻要原型上存在此對象的構造器它就傳回true,但如果跨文檔比較,iframe裡面的數組執行個體就不是父視窗的array的執行個體。第三組有關constructor的陷阱,在舊版本ie下dom與bom對象的constructor屬性是沒有暴露出來的。最後有關nan,nan對象與null、undefined一樣,在序列化時是原樣輸出的,但isnan這方法非常不靠譜,把字元串、對象放進去也傳回true,這對我們序列化非常不利。

另外,在ie下typeof還會傳回unknow的情況。

基于這ie的特性,我們可以用它來判定某個vbscript方法是否存在。

另外,以前人們總是以document.all是否存在來判定ie,這其實是很危險的。因為用document.all來取得頁面中的所有元素是不錯的主意,這個方法firefox、chrome觊觎好久了,不過人們都這樣判定,于是有了在chrome下的這出鬧劇。

在判定undefined、null、string、number、boolean、function這6個還算簡單,前面兩個可以分别與void(0)、null比較,後面4個直接typeof也可滿足90%的情形。這樣說是因為string、number、boolean可以包裝成“僞對象”,typeof無法按照我們的意願工作了,雖然它嚴格執行了 ecmascript 的标準。

這些還是最簡單的,難點在于regexp與array。判定regexp類型的情形很少,不多講了,array則不一樣。有關isarray的實作不下二十種,都是因為javascript的鴨子類型被攻破了。直到prototype.js把object.prototype.tostring發掘出來,此方法是直接輸出對象内部的[[class]],絕對精準。有了它,可以跳過95%的陷阱了。

isarray早些年的探索:

至于null、undefined、nan直接這樣:

最後要判定的對象是window,由于ecma是不規範 host 對象,window 對象屬于 host ,是以也沒有被約定,就算object.prototype.tostring也對它無可奈何。

[object object]ie6

[object object]ie7

[object object]ie8

[object window]ie9

[object window]firefox3.6

[object window]opera10

[object domwindow]safai4.04

[object global]chrome5.0.3.22

不過根據window.window和window.setinterval去判定更加不夠譜,用一個技巧我們可以完美識别ie6、ie7、ie8的window對象,其他還是用tostring,這個神奇的hack(技巧)就是,window與document互相比較,如果順序不一樣,其結果是不一樣的!

當然,如果細數起來,javascript匪夷所思的事比比都是。

存在a !== a的情況;

存在a == b && b != a的情況;

存在a == !a的情況;

存在a === a+100的情況;

1 < 2 < 3為true, 3 > 2 > 1為false;

0/0為nan;

……

好了,至此,所有重要的isxxx問題都解決了,剩下的就把它們表達出來。經典做法就是直接羅列。

在prototype.js中,擁有iselement、isarray、ishash、isfunction、isstring、isnumber、isdate、isundefined方法。

mootools搞了個typeof判定基本類型,instanceof判定自定義“類”。

rightjs有isfunction 、ishash、isstring、isnumber、isarray 、iselement 、isnode。

ext有isempty、isarray、isdate、isobject、issimpleobject、isprimitive、isprimitive、isfunction、isnumber、isnumeric、isstring、isboolean、iselement、istextnode、isdefined、isiterable,應有盡有。最後,還有typeof判定基本類型。

underscore.js有iselement、isempty、isarray、isarguments、isobject、isfunction、isstring、isnumber、isfinite、isnan、isboolean、isdate、isregexp、isnull、isundefined。

isxxx系列就像惡性惡性良性腫瘤一樣不斷膨脹,其實你多弄幾個isxxx也不能滿足使用者的全部需求。就像isdate、isregexp會用到的機率有多高呢?

jquery就不與其他架構一樣了,在jquery 1.4中隻有isfunction、isarray、isplainobject、isemptyobject。isfunction、isarray肯定是使用者用得最多,isplainobject則是用來判定是否為純淨的javascript對象,既不是dom、bom對象,也不是自定義“類”的執行個體對象,制造它的最初目的是用于深拷貝,避開像window那樣自己引用自己的對象。isemptyobject是用于資料緩存系統,當此對象為空時,就可以删除它。

在avalon.mobile中有一個更精簡的版本,由于它隻支援ie10等非常新的浏覽器,就沒有幹擾因素了,可以大膽使用ecma262v5的新api。

isarraylike也是一個常用的方法,但判定一個類數組太難了,唯一的辨識手段是它應該有一個大于或等于零的整型length屬性。此外還有一些“共識”,如window與函數和元素節點(如form元素)不算類數組,雖然它們都滿足前面的條件。是以至今jquery沒有把它暴露出來。

補充一句,1.3版本中,prototype.js的研究成果(object.prototype.tostring.call)就應用于jquery了。在1.2版本中,jquery判定一個變量是否為函數非常複雜。

jquery1.43引入iswindow來處理makearray中對window的判定,引入isnan用于確定樣式指派的安全。同時引入type代替typeof關鍵字,用于擷取資料的基本類型。

jquery1.7中添加isnumeric代替isnan。這是個不同于其他架構的isnumber,它可以是字元串,隻要外觀上像數字就行了。但jquery1.7還做了一件違背之前提到穩定性的事情,貿然去掉jquery.isnan ,是以導緻基于舊版本 jquery 的一大批插件失效。

mass framework的思路與jquery一緻,盡量減少isxxx系列的數量,把iswindow、isnan、nodename等方法都整進去了。這是個野心勃勃的方法,代碼比較長,它既可以擷取類型,也可以傳入第二參數進行類型比較。

然後type方法就輕松了,用tostring.call(obj)得出的值作鍵,直接從映射中取。隻有在ie6、ie7、ie8中,我們才費一些周折處理window、document、arguments、nodelist等對象。當然,這隻是在種子子產品的情形,在語言子產品,mass framework還是會添加isarray、isfunction這兩個著名api,此外還有isplainobject、isnative、isemptyobject、isarraylike這4個方法,在選擇器子產品,還追加isxml方法。

基于實用主義,我們有時不得不妥協。百度的tangram就是典型, 與ext一樣,能想到的都寫上,而且判定非常嚴謹。

上一篇: JSON
下一篇: JSON

繼續閱讀