歡迎光臨
每天分享高質量文章

JS陣列的map相容註意事項以及“陣列也是物件”的理解

作者:slife的部落格

網址:http://segmentfault.com/a/1190000002995303

點選“閱讀原文”可檢視本文網頁版


先說結論吧:

陣列的map方法在IE9以下是不支援的,因此需要寫一個相容方法來實現此行為,在實現相容的時候:必須註意:對於陣列中被刪除(delete)或者根本從未賦值的索引項,map中第一個函式引數是不會執行的。

關於這一點,在ECMA標準和MDN參考檔案都是有說明的:

MDN:

it is not invoked for indexes that are undefined, those which have been deleted or which have never been assigned values.

ECMA:

callbackfn is called only for elements of the array which actually exist; it is not called for missing elements of the array.

我看到一些用來相容的方法,都沒有註意到這種情形,那麼不註意這種情形會出現什麼問題呢?為什麼MDN上給的相容方法會這麼曲折呢?

我先把我在培訓班的老師給出的相容方法貼出來:

Array.prototype.myMap = function (fn, context) {

context = context || window;

var ary = [];

if (Array.prototype.map) {

ary = this.map(fn, context);

} else {

for (var i = 0; i < this.length; i++) {

ary[i] = fn.apply(context, [this[i], i, this]);

}

}

return ary;

}

這個實現比較簡單,流程很清晰,如果瀏覽器有原生陣列map方法,呼叫該方法,否則執行else中的陳述句;但是如果是下麵這樣一個陣列,在沒有實現原生map方法的瀏覽器中,結果跟原生map方法呼叫不一樣:

var arr=[1,2,,,3];

console.dir(arr.myMap(function(e,i,a){return e*2;}));

原生map方法執行上述程式碼的輸出是

而如果瀏覽器不支援原生map方法,會執行else陳述句,這樣的輸出是:

你看,這兩者執行區別就在於一開始的結論:原生的map方法跳過了那些從未賦值的索引對應的項。

陣列也是物件!!

簡單來說,物件就是一系列屬性名值對,即某個屬性名對應某個屬性值;當我們遍歷物件時,不在物件中的屬性當然不會被訪問到。

而在JS中,陣列就是物件,甚至陣列的一些遍歷方法,在內部執行的時候,都是先將陣列轉化為物件,

var O=Object(this);

然後遍歷陣列物件中,所有已經定義的,且索引為數字的項。在絕大部分情況下,這些數字剛好是連續的。那什麼時候會出現不連續呢?我所知道有這兩種;

  1. 以字面量方式新建立陣列的時候,連續的逗號,會形成數字索引不連續;如var arr=[1,2,,,3],你可以在瀏覽器控制臺中試試輸出console.dir([1,2,,,3])的結果(註意,一定要用console.dir方法);
  2. 利用delete運運算元刪除陣列中的某項。我們知道delete運運算元用於刪除物件中某個屬性,而JS中,陣列就是物件的一種,陣列的索引就是其屬性名,對應的項就是屬性值。用delete刪除後,陣列中的這對屬性名值對(又稱鍵值對)就不存在了,這時候對其採用遍歷方法,當然不會針對不存在名值對進行呼叫了。

有一點要特別註意,陣列中被刪除的項或者從未定義的項,與人為將其值設定為undefined的項,儘管如果強行訪問,結果都是undefined,但是是不太一樣的,比如這樣一個陣列,[1,,undefined];,該陣列(現在我們可以更準確得說:陣列物件)中不存在索引為1的項,但是卻存在索引為2的項,可以用console.dir輸出看出區別。

陣列的length屬性還能反映陣列長度嗎?

說到上面這些,你肯定已經意識到了,既然陣列的索引可以被跳過,那陣列的length屬性還能反映其長度嗎?

關於這個問題,我是這麼理解的,

  1. 陣列的length屬性並不能反應陣列中元素的數目,在絕大多數情況(即沒有人工幹預length屬性,且陣列中沒有跳過的索引),它只是剛好,剛好等於最大的索引加一。當我們從物件的角度來看待陣列時,像0、1、2、3、4、5、6等等這些數字索引和length一樣,都只是陣列物件的屬性。
  2. 用delete運運算元刪除陣列任意一項,或者將任意一項值賦值為undefined,length不改變;
  3. 當人為地設定陣列的length屬性值時,length隨之改變,同時索引不小於該值的都會被從陣列中徹底刪除。

贊(0)

分享創造快樂