JavaScript中的數組與偽數組的區別


在JavaScript中,除了5種原始數據類型之外,其他所有的都是對象,包括函數(Function)。

基本數據類型:String,boolean,Number,Undefined, Null

引用數據類型:Object(Array,Date,RegExp,Function)

在這個前提下,咱們再來討論JavaScript的對象。

1、創建對象

var obj = {}; //種方式創建對象,被稱之為對象直接量(Object Literal)
var obj = new Object(); // 創建一個空對象,和{}一樣

更多創建對象的知識,參見《JavaScript權威指南(第6版)》第6章

2、創建數組

var arr = [];//這是使用數組直接量(Array Literal)創建數組
var arr = new Array();//構造函數Array() 創建數組對象

 更多創建數組的知識,參見《JavaScript權威指南(第6版)》第7章。

3、對象與數組的關系

在說區別之前,需要先提到另外一個知識,就是JavaScript的原型繼承。所有JavaScript的內置構造函數都是繼承自 Object.prototype。在這個前提下,可以理解為使用 new Array() 或 [] 創建出來的數組對象,都會擁有 Object.prototype 的屬性值。

對於題主的問題意味着:

var obj = {};// 擁有Object.prototype的屬性值
var arr = [];
//使用數組直接量創建的數組,由於Array.prototype的屬性繼承自 Object.prototype,
//那么,它將同時擁有Array.prototype和Object.prototype的屬性值

可以得到對象和數組的第一個區別:對象沒有數組Array.prototype的屬性值

4、什么是數組

數組具有一個最基本特征:索引,這是對象所沒有的,下面來看一段代碼:

var obj = {};
var arr = [];

obj[2] = 'a';
arr[2] = 'a';

console.log(obj[2]); // 輸出 a
console.log(arr[2]); // 輸出 a
console.log(obj.length); // 輸出 undefined
console.log(arr.length); // 輸出 3

通過上面這個測試,可以看到,雖然 obj[2]與arr[2] 都輸出'a',但是,在輸出length上有明顯的差異,這是為什么呢?

obj[2]與arr[2]的區別

  • obj[2]輸出'a',是因為對象就是普通的鍵值對存取數據

  • 而arr[2]輸出'a' 則不同,數組是通過索引來存取數據,arr[2]之所以輸出'a',是因為數組arr索引2的位置已經存儲了數據

obj.length與arr.length的區別

  • obj.length並不具有數組的特性,並且obj沒有保存屬性length,那么自然就會輸出undefined

  • 而對於數組來說,length是數組的一個內置屬性,數組會根據索引長度來更改length的值。

為什么arr.length輸出3,而不是1呢?

  • 這是由於數組的特殊實現機制,對於普通的數組,如果它的索引是從0開始連續的,那么length的值就會等於數組中元素個數

  • 而對於上面例子中arr,在給數組添加元素時,並沒有按照連續的索引添加,所以導致數組的索引不連續,那么就導致索引長度大於元素個數,那么我們稱之為稀疏數組。

有關稀疏數組的特性就不再討論更多,參見《JavaScript權威指南(第6版)》7.3節。

5、偽數組

定義:

1、擁有length屬性,其它屬性(索引)為非負整數(對象中的索引會被當做字符串來處理,這里你可以當做是個非負整數串來理解)
2、不具有數組所具有的方法

偽數組,就是像數組一樣有 length 屬性,也有 0123 等屬性的對象,看起來就像數組一樣,但不是數組,比如

var fakeArray = {
    length: 3,
    "0": "first",
    "1": "second",
    "2": "third"
};

for (var i = 0; i < fakeArray.length; i++) {
    console.log(fakeArray[i]);
}

Array.prototype.join.call(fakeArray,'+');

常見的參數的參數 arguments,DOM 對象列表(比如通過 document.getElementsByTags 得到的列表),jQuery 對象(比如 $("div"))。

偽數組是一個 Object,而真實的數組是一個 Array

fakeArray instanceof Array === false;
Object.prototype.toString.call(fakeArray) === "[object Object]";

var arr = [1,2,3,4,6];
arr instanceof Array === true;
Object.prototype.toString.call(arr) === "[object Array]"

《javascript權威指南》上給出了代碼用來判斷一個對象是否屬於“類數組”。如下:

// Determine if o is an array-like object.
// Strings and functions have numeric length properties, but are 
// excluded by the typeof test. In client-side JavaScript, DOM text
// nodes have a numeric length property, and may need to be excluded 
// with an additional o.nodeType != 3 test.
function isArrayLike(o) {    
	if (o &&                                // o is not null, undefined, etc.
	        typeof o === 'object' &&            // o is an object
	        isFinite(o.length) &&               // o.length is a finite number
	        o.length >= 0 &&                    // o.length is non-negative
	        o.length===Math.floor(o.length) &&  // o.length is an integer
	        o.length < 4294967296)              // o.length < 2^32
	        return true;                        // Then o is array-like
	else
	        return false;                       // Otherwise it is not
}

 不過有個更簡單的辦法來判斷,用 Array.isArray

Array.isArray(fakeArray) === false;
Array.isArray(arr) === true;

 從外觀上看偽數組,看不出來它與數組的區別,在JavaScript內置對象中常見的偽數組就是大名鼎鼎的auguments:

(function() {
  console.log(typeof arguments); // 輸出 object,它並不是一個數組
}());

 另外在DOM對象中,childNodes也是偽數組

console.log(typeof document.body.childNodes); // 輸出 object

除此之外,還有很多常用的偽數組,就不一一列舉。

偽數組存在的意義,是可以讓普通的對象也能正常使用數組的很多算法,比如:

var arr = Array.prototype.slice.call(arguments)

或者
var arr = Array.prototype.slice.call(arguments, 0); // 將arguments對象轉換成一個真正的數組

Array.prototype.forEach.call(arguments, function(v) {
  // 循環arguments對象
});

 除了使用 Array.prototype.slice.call(arguments),你也可以簡單的使用[].slice.call(arguments) 來代替。另外,你可以使用 bind 來簡化該過程。

var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

function list() {
  return slice(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

將具有length屬性的對象轉換成數組對象,arguments是每個函數在運行的時候自動獲得的一個近似數組的對象(傳入函數的參數從0開始按數字排列,而且有length)。

比如當你 func('a', 'b', 'c') 的時候,func里面獲得的arguments[0] 是 'a',arguments[1] 是 'b',依次類推。但問題在於這個arguments對象其實並不是Array,所以沒有slice方法。Array.prototype.slice.call( )可以間接對其實現slice的效果,而且返回的結果是真正的Array

對於IE9以前的版本(DOM實現基於COM),我們可以使用makeArray來實現。

// 偽數組轉化成數組
var makeArray = function(obj) {    
	if (!obj || obj.length === 0) {        
		return [];
	}    
	// 非偽類對象,直接返回最好
	if (!obj.length) {        
		return obj;
	}    
	// 針對IE8以前 DOM的COM實現
	try {        
		return [].slice.call(obj);
	} catch (e) {        
		var i = 0,
	    	j = obj.length,
	    	res = [];        
		for (; i < j; i++) {
			res.push(obj[i]);
		}
		return res;
	}

};

 更多關於偽數組的知識,參見《JavaScript權威指南(第6版)》7.11節。

6、總結

  • 對象沒有數組Array.prototype的屬性值,類型是Object,而數組類型是Array;

  • 數組是基於索引的實現,length會自動更新,而對象是鍵值對;

  • 使用對象可以創建偽數組,偽數組可以正常使用數組的大部分方法;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM