標籤:dom 語言 console 告訴 mat null lan 自身 []
很多時候我們需要對JavaScript中資料類型( Function 、 String 、 Number 、 Undefined 、 Boolean 和 Object )做判斷。在JavaScript中提供了typeof操作符可以對這些常用的資料類型做判斷。但要使用typeof來判斷資料是不是一個數組,就不起作用了。那在實際生產中要如何來檢測資料是不是一個數組呢?
今天的學習任務就是如何來檢測一個資料是不是數組?
typeof操作符
typeof 可以解決大部分資料類型的檢測,如:
1 console.log(typeof function () {return;}); // function 2 console.log(typeof "a"); // string 3 console.log(typeof 123); // number 4 console.log(typeof a); //undefined 5 console.log(typeof true); // boolean 6 console.log(typeof NaN); // number 7 console.log(typeof !NaN); //boolean 8 console.log(typeof {name:"大漠",age: "37"}); // object 9 console.log(typeof ["大漠","37"]); // object10 console.log(typeof null); // object
上面的程式碼範例告訴我們,typeof {name:"大漠",age: "37"}和typeof ["大漠","37"]返回的都是object。事實再次證明typeof沒辦法對數組進行檢測,那麼這裡引出一個問題?如何判斷資料是個數群組類型?
檢測數組方法
雖然typeof操作符無法檢測數組,但事實上檢測數組方法有很多種。@Tom、@John、@Rick、@Ken和@Eric在Quora的一篇文章中總結了五種不同的檢測數組的方法。接下來我們一起來瞭解和學習這幾種檢測數組的方法。
ECMAScript 5的isArray函數
1 function isArray(obj) {2 return Array.isArray(obj);3 }4 var arr = ["大漠","w3cplus"]; //建立一個數組5 isArray(arr); // true
毫無疑問,這看起來最完美的解決方案,因為他是原生的。ECMAScript 5將Array.isArray()引入JavaScript。但其相容性令你會感到些許的失望:IE9+、 Firefox 4+、Safari 5+、Opera 10.5+和Chrome都實現了這個方法,但是在IE8之前的版本是不支援的。
在這個基礎上對建構函式做一下檢測,而且這個檢測過程非常的快,而且也非常的準確。事實上對我們的使用太準確了。但在工作是不能確定一個變數是繼承自一個數組。這樣一來,在某種程度上對建構函式做檢測對於我們自己來說是很需要的,也是非常有益的:
1 function isArray(obj) {2 return (typeof obj !== ‘undefined‘ && obj && obj.constructor === Array);3 }對象自身的constructor屬性
上面的樣本中,檢測建構函式時使用了對像自身的constructor屬性。其實constructor屬性返回一個指向建立了該對象原型的函數引用。使用該屬性也可以檢測數群組類型。
1 var arr = ["大漠","W3cplus"];2 console.log(arr.constructor === Array); // true
instanceof操作符
除了使用對像自身的 constructor 屬性檢測一個數組之外,還可以使用 instanceof 操作符來檢測一個數組。
instanceof 操作符可以用來判斷某個建構函式的 prototype 屬性是否存在另外一個要檢測對象的原型鏈上。也就是判斷instanceof前面的對象是否是後面的類或對象的執行個體。
註:這個操作符和JavaScript中物件導向有點關係,瞭解這個就先得瞭解JavaScript中的物件導向。
來回憶下 instanceof 運算子的使用方式。a instanceof b,如果返回 true,表示 a 是 b 的一個執行個體。那麼如果 a instanceof Array 返回 true,是不是就說明 a 是 數群組類型呢?
1 var arr = ["大漠","W3cplus"];2 console.log(arr instanceof Array); // true
跨frame執行個體化對象帶來的問題
constructor 和 instanceof 貌似很好的兩個檢測數組的方法,但實際上還是有些漏洞的,當你在多個frame中回來跳的時候,這兩種方法就慘了。由於每一個frame都有自己的一套執行環境,跨frame執行個體化的對象彼此並不共用原型鏈,通過instanceof操作符和constructor屬性檢測的方法自然會失敗。
1 // 建立iframe並添加到DOM中2 var iframe = document.createElement(‘iframe‘); //建立iframe3 document.body.appendChild(iframe); //將建立的iframe添加到body中4 otherArray = window.frames[window.frames.length - 1].Array;5 var arr = new otherArray("大漠","W3cplus"); //聲明數組["大漠","W3cplus"]6 console.log(arr instanceof Array); // false7 console.log(arr.constructor === Array); // false對象原生toString檢測
Object.prototype.toString 的行為:首先,取得對象的一個內部屬性 [[Class]] ,然後依據這個屬性,返回一個類似於 "[object Array]" 的字串作為結果(看過ECMA標準的應該都知道,[[]]用來表示語言內部用到的、外部不可直接存取的屬性,稱為“內部屬性”)。利用這 個方法,再配合call,我們可以取得任何對象的內部屬性 [[Class]] ,然後把類型檢測轉化為字串比較,以達到我們的目的。
1 isArray = function(obj) {2 return Object.prototype.toString.call(obj) == "[object Array]";3 }4 var arr = ["大漠","W3cplus"];5 console.log(isArray(arr)); // true
call 改變 toString 的this引用為待檢測的對象,返回此對象的字串表示,然後對比此字串是否是 [object Array] ,以判斷其是否是Array的執行個體。為什麼不直接o.toString()?嗯,雖然Array繼承自Object,也會有toString方法,但是這個方法有可能會被改寫而達不到我們的要求,而 Object.prototype 則是老虎的屁股,很少有人敢去碰它的,所以能一定程度保證其“純潔性”:)
JavaScript 標準文檔中定義: [[Class]] 的值只可能是下面字串中的一個:Arguments, Array, Boolean, Date,Error, Function, JSON, Math, Number, Object, RegExp, String。
其它方法
除了上面介紹的一些檢測數組的方法之外,還有:
@Rick Waldron提供的:
1 var arr = [1,2,3]; 2 function isArray( arg ) { 3 if ( typeof arg === "object" && 4 ( "join" in arg && typeof arg.join === "function" ) && 5 ( "length" in arg && typeof arg.length === "number" ) ) { 6 return true; 7 } 8 return false; 9 }10 console.log(true, isArray(arr)); // true true11 console.log(false, isArray({join: true}) ); // false false12 console.log(false, isArray({join: function () {return false;}}) ); // false false
@Shamasis Bhattacharya 提供的:
1 var isArray = function (subj) { 2 try { 3 subj && (subj.length = -1); 4 return false; 5 } 6 catch (er) { 7 return true; 8 } 9 };10 var arr = [1,2,3];11 isArray(arr); // true
道格拉斯提供的:
1 var is_array = function (value) {2 return value &&3 typeof value === ‘object‘ &&4 typeof value.length === ‘number‘ &&5 typeof value.splice === ‘function‘ &&6 !(value.propertyIsEnumerable(‘length‘));7 };8 var arr = [1,2,3];9 is_array(arr); // true最佳檢測方法
其實也沒有什麼是最佳檢測方法,只有最合適的檢測方法。綜合上面各種檢測數組的方法,稍做一些處理:
1 var isArray = (function () { 2 if (Array.isArray) { 3 return Array.isArray; 4 } 5 var objectToStringFn = Object.prototype.toString, 6 arrayToStringResult = objectToStringFn.call([]); 7 8 return function (subject) { 9 return objectToStringFn.call(subject) === arrayToStringResult;10 };11 }());12 13 var arr = [];14 isArray(arr); // true
最佳化的方法就是不管Array.isArray‘是否能用,都可以回到對象原生toString檢測和對象原生toString`檢測上。
JavaScript學習筆記:檢測數組方法