如何判斷一個物件是陣列

2020-10-11 15:00:13


一、typeof方法

當涉及到檢測資料的問題時,我們首先想到的可能就是用typeof來檢測資料型別。

其中typeof返回範例如下:

    // 數值
    typeof 23 === 'number';

    // 字串
    typeof '' === 'string';

    // 布林值
    typeof false === 'boolean';

    // Symbols
    typeof Symbol() === 'symbol';

    // Undefined
    typeof undefined === 'undefined';

    // 物件
    typeof { a: 1 } === 'object';
    typeof [1, 2, 4] === 'object';

    // 函數
    typeof function () { } === 'function';
//注意:    
    // 下面的例子令人迷惑,非常危險,沒有用處。避免使用它們。
    typeof new Boolean(true) === 'object';
    typeof new Number(1) === 'object';
    typeof new String('abc') === 'object';

由上例我們可以看出,對於Function, String, Number, Undefined等這幾種基本型別來說,我們可以採用typeof檢測。
但是對於陣列或者正則(由一個字元序列形成的搜尋模式)來說,typeof檢測無法滿足,因為兩者的返回型別都是物件object,所以我們還需另找方法…


二、Instanceof與constructor屬性

1.Instanceof

instanceof可以判斷一個建構函式的prototype屬性所指向的物件是否存在另外一個要檢測物件的原型鏈上,所以用來檢測某物件是否是陣列可能更好不過了,直接通過看返回值true與false直接可以判斷是否是陣列。

程式碼如下(範例):

        console.log([] instanceof Array); // true
        console.log({ a: 1 }instanceof Array); // false

2.constructor屬性

同樣 由於js中每一個物件都有一個constructor屬性,它參照了初始化該物件的建構函式,比如判斷未知物件的型別。
程式碼如下(範例):

          //方法重寫
          function isArray(obj) {
            return typeof obj == 'object' && obj.constructor == Array
          }
          // 測試de
          console.log(isArray([])); // true
          var a = {"a":1};
          console.log(isArray(a));   // false
           
          var b = [1,2,3];
          console.log(isArray(b));       // true
          console.log(isArray(/\d+/g));  // false

如上可以看到,通過呼叫prototype屬性與isArray 方法一般情況下可以判斷是否為陣列的列子

注意

但是這種一般情況真的可以完全判斷嗎?答案當然是否定的,當對於跨框架iframe的時候使用頁面中的陣列時:
程式碼如下(範例):

           var iframe = document.createElement('iframe');
            document.body.appendChild(iframe);
            xArray = window.frames[window.frames.length-1].Array;    
            var arr = new xArray("1","2","3","4","5");
            //這個寫法IE下是不支援的,標準瀏覽器firefox,chrome下有
             
             
            console.log(arr); // 列印出 ["1", "2", "3", "4", "5"]
            console.log(arr instanceof Array); // false 
            console.log(arr.constructor === Array); // false     

失敗的關鍵在於iframe元素的跨平臺的特點,使其有一套自己的執行環境,建立的陣列無法共用其prototype屬性,由於無法共用原型鏈,所以無法採用如上兩種方式判斷。


三、Object.prototype.toString.call()方法

以上三種方法都無法準確判斷物件是否為陣列,有沒有比較準確的方法呢,答案當然是肯定的。

        function isArray(obj) {
      return Object.prototype.toString.call(obj) == '[object Array]';          //call是用來改變指向的
    }      //方法重寫
// 程式碼呼叫
          //一般情況
    console.log(isArray([])); // true
    console.log(isArray([1,2,3])); // true
     
         //判斷特殊情況,跨平臺框架
    var iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    xArray = window.frames[window.frames.length-1].Array;    
    var arr = new xArray("1","2","3","4","5");
     
    console.log(arr);       // ["1","2","3","4","5"]
    console.log(isArray(arr)); // true       ~   

這時候,可能又有人要說了,這種原型鏈的方法這麼麻煩,有簡單一點的嗎,當然ECMA-262為我們引入了Array.isArray()方法。


四、Array.isArray()

isArray方法用來準確判斷一個值是否為陣列,對於IE9+、 Firefox 4+、Safari 5+、Opera 10.5+和Chrome都可以完美相容,但是對於IE8之前的版本是不支援的。
但是個人感覺用法來說是最為簡便的:

          Array.isArray([])   // true    

總結

對於判斷一個物件是陣列的問題上,最好的方式如下:

       var arr = [1,2,3];
       var arr2 = [{ name : 'jack', age : 22 }];
       function isArrayFn(value){
    // 判斷Array.isArray的相容性
    if (typeof Array.isArray === "function") {
        return Array.isArray(value);
    }else{
        return Object.prototype.toString.call(value) === "[object Array]";
        // return obj.__proto__ === Array.prototype;
    }
}
console.log(isArrayFn(arr));  // true
console.log(isArrayFn(arr2));  // true       

補充:原生鏈的方法不僅看起來較為麻煩,而且執行效率也幾乎慢了Array.isArray()一倍,所以我們為了提高程式碼的執行效率,所以我們可以最先判斷Array.isArray()的相容性問題,之後再考慮選取原型鏈的方法。