ES5中新增的Array方法詳細說明

原文出處:來自張鑫旭-鑫空間-鑫生活這篇文章(ES5中新增的Array方法詳細說明)

前言-索引

ES5中新增的很多東西,瞭解之對咱們寫JavaScript會有很多幫助,好比數組這塊,咱們可能就不須要去有板有眼地for循環了。javascript

ES5中新增了寫數組方法,以下:html

  1. forEach (js v1.6)
  2. map (js v1.6)
  3. filter (js v1.6)
  4. some (js v1.6)
  5. every (js v1.6)
  6. indexOf (js v1.6)
  7. lastIndexOf (js v1.6)
  8. reduce (js v1.8)
  9. reduceRight (js v1.8)

瀏覽器支持java

  • Opera 11+
  • Firefox 3.6+
  • Safari 5+
  • Chrome 8+
  • Internet Explorer 9+

對於讓人失望不少次的IE6-IE8瀏覽器,Array原型擴展能夠實現以上所有功能,例如forEach方法:數組

// 對於古董瀏覽器,如IE6-IE8

if (typeof Array.prototype.forEach != "function") {
  Array.prototype.forEach = function () {
    /* 實現 */
  };
}

一個一個來

  1. forEach
    forEach是Array新方法中最基本的一個,就是遍歷,循環。例以下面這個例子:
    [1, 2 ,3, 4].forEach(alert

    等同於下面這個傳統的for循環:瀏覽器

    var array = [1, 2, 3, 4];
    
    for (var k = 0, length = array.length; k < length; k++) {
      alert(array[k]);
    }

    Array在ES5新增的方法中,參數都是function類型,默認有傳參,這些參數分別是?見下面:dom

    [1, 2 ,3, 4].forEach(console.log);
    
    // 結果:
    
    // 1, 0, [1, 2, 3, 4]
    // 2, 1, [1, 2, 3, 4]
    // 3, 2, [1, 2, 3, 4]
    // 4, 3, [1, 2, 3, 4]

    用來判斷參數個數以及內容的測試輸出截圖 張鑫旭-鑫空間-鑫生活

    顯而易見,forEach方法中的function回調支持3個參數,第1個是遍歷的數組內容;第2個是對應的數組索引,第3個是數組自己。ecmascript

    所以,咱們有:wordpress

    [].forEach(function(value, index, array) {
        // ...
    });

    對比jQuery中的$.each方法:函數

    $.each([], function(index, value, array) {
        // ...
    });

    會發現,第1個和第2個參數正好是相反的,你們要注意了,不要記錯了。後面相似的方法,例如$.map也是如此。oop

    如今,咱們就可使用forEach賣弄一個稍顯完整的例子了,數組求和:

    var sum = 0;
    
    [1, 2, 3, 4].forEach(function (item, index, array) {
      console.log(array[index] == item); // true
      sum += item;
    });
    
    alert(sum); // 10

    再下面,更進一步,forEach除了接受一個必須的回調函數參數,還能夠接受一個可選的上下文參數(改變回調函數裏面的this指向)(第2個參數)。

    array.forEach(callback,[ thisObject])

    例子更能說明一切:

    var database = {
      users: ["張含韻", "江一燕", "李小璐"],
      sendEmail: function (user) {
        if (this.isValidUser(user)) {
          console.log("你好," + user);
        } else {
          console.log("抱歉,"+ user +",你不是本家人");	
        }
      },
      isValidUser: function (user) {
        return /^張/.test(user);
      }
    };
    
    // 給每一個人法郵件
    database.users.forEach(  // database.users中人遍歷
      database.sendEmail,    // 發送郵件
      database               // 使用database代替上面標紅的this
    );
    
    // 結果:
    // 你好,張含韻
    // 抱歉,江一燕,你不是本家人
    // 抱歉,李小璐,你不是本家

    若是這第2個可選參數不指定,則使用全局對象代替(在瀏覽器是爲window),嚴格模式下甚至是undefined.

    另外,forEach不會遍歷純粹「佔着官位吃空餉」的元素的,例以下面這個例子:

    var array = [1, 2, 3];
    
    delete array[1]; // 移除 2
    alert(array); // "1,,3"
    
    alert(array.length); // but the length is still 3
    
    array.forEach(alert); // 彈出的僅僅是1和3

    綜上所有規則,咱們就能夠對IE6-IE8進行仿真擴展了,以下代碼:

    // 對於古董瀏覽器,如IE6-IE8
    
    if (typeof Array.prototype.forEach != "function") {
      Array.prototype.forEach = function (fn, context) {
        for (var k = 0, length = this.length; k < length; k++) {
          if (typeof fn === "function" && Object.prototype.hasOwnProperty.call(this, k)) {
            fn.call(context, this[k], k, this);
          }
        }
      };
    }

    如今拿上面「張含韻」的例子測下咱們擴展的forEach方法,您可能狠狠地點擊這裏:兼容處理的forEach方法demo

    例如IE7瀏覽器下:
    IE7下forEach方法測試結果截圖 張鑫旭-鑫空間-鑫生活

  2. map
    這裏的map不是「地圖」的意思,而是指「映射」。[].map(); 基本用法跟forEach方法相似:
    array.map(callback,[ thisObject]);

    callback的參數也相似:

    [].map(function(value, index, array) {
        // ...
    });

    map方法的做用不難理解,「映射」嘛,也就是原數組被「映射」成對應新數組。下面這個例子是數值項求平方:

    var data = [1, 2, 3, 4];
    
    var arrayOfSquares = data.map(function (item) {
      return item * item;
    });
    
    alert(arrayOfSquares); // 1, 4, 9, 16

    callback須要有return值,若是沒有,就像下面這樣:

    var data = [1, 2, 3, 4];
    var arrayOfSquares = data.map(function() {});
    
    arrayOfSquares.forEach(console.log);

    結果以下圖,能夠看到,數組全部項都被映射成了undefined
    所有項都成了undefined

    在實際使用的時候,咱們能夠利用map方法方便得到對象數組中的特定屬性值們。例以下面這個例子(以後的兼容demo也是該例子):

    var users = [
      {name: "張含韻", "email": "zhang@email.com"},
      {name: "江一燕",   "email": "jiang@email.com"},
      {name: "李小璐",  "email": "li@email.com"}
    ];
    
    var emails = users.map(function (user) { return user.email; });
    
    console.log(emails.join(", ")); // zhang@email.com, jiang@email.com, li@email.com

    Array.prototype擴展可讓IE6-IE8瀏覽器也支持map方法:

    if (typeof Array.prototype.map != "function") {
      Array.prototype.map = function (fn, context) {
        var arr = [];
        if (typeof fn === "function") {
          for (var k = 0, length = this.length; k < length; k++) {      
             arr.push(fn.call(context, this[k], k, this));
          }
        }
        return arr;
      };
    }

    您能夠狠狠地點擊這裏:兼容map方法測試demo

    結果顯示以下圖,IE6瀏覽器:
    map映射方法測試,IE6下截圖

  3. filter
    filter爲「過濾」、「篩選」之意。指數組filter後,返回過濾後的新數組。用法跟map極爲類似:
    array.filter(callback,[ thisObject]);

    filtercallback函數須要返回布爾值truefalse. 若是爲true則表示,恭喜你,經過啦!若是爲false, 只能高歌「我只能無情地將你拋棄……」

    可能會疑問,必定要是Boolean值嗎?咱們能夠簡單測試下嘛,以下:

    var data = [0, 1, 2, 3];
    var arrayFilter = data.filter(function(item) {
        return item;
    });
    console.log(arrayFilter); // [1, 2, 3]

    有此可見,返回值只要是弱等於== true/false就能夠了,而非非得返回 === true/false.

    所以,咱們在爲低版本瀏覽器擴展時候,無需關心是否返回值是不是純粹布爾值(見下黑色代碼部分):

    if (typeof Array.prototype.filter != "function") {
      Array.prototype.filter = function (fn, context) {
        var arr = [];
        if (typeof fn === "function") {
           for (var k = 0, length = this.length; k < length; k++) {
              fn.call(context, this[k], k, this) && arr.push(this[k]);
           }
        }
        return arr;
      };
    }

    接着上面map篩選郵件的例子,您能夠狠狠地點擊這裏:兼容處理後filter方法測試demo

    主要測試代碼爲:

    var emailsZhang = users
      // 得到郵件
      .map(function (user) { return user.email; })
      // 篩選出zhang開頭的郵件
      .filter(function(email) {  return /^zhang/.test(email); });
    
    console.log(emailsZhang.join(", ")); // zhang@email.com

    filter demo頁面的結果截圖 張鑫旭-鑫空間-鑫生活

    實際上,存在一些語法糖能夠實現map+filter的效果,被稱之爲「數組簡約式(Array comprehensions)」。目前,僅FireFox瀏覽器能夠實現,展現下又不會懷孕:

    var zhangEmails = [user.email for each (user in users) if (/^zhang/.test(user.email)) ];
    
    console.log(zhangEmails); // [zhang@email.com]
  4. some
    some意指「某些」,指是否「某些項」合乎條件。與下面的every算是好基友,every表示是否「每一項」都要靠譜。用法以下:
    array.some(callback,[ thisObject]);

    例以下面的簡單使用:

    var scores = [5, 8, 3, 10];
    var current = 7;
    
    function higherThanCurrent(score) {
      return score > current;
    }
    
    if (scores.some(higherThanCurrent)) {
      alert("朕準了!");
    }

    結果彈出了「朕準了」文字。 some要求至少有1個值讓callback返回true就能夠了。顯然,8 > 7,所以scores.some(higherThanCurrent)值爲true.

    咱們天然可使用forEach進行判斷,不過,相比some, 不足在於,some只有有true即返回再也不執行了。

    IE6-IE8擴展以下:

    if (typeof Array.prototype.some != "function") {
      Array.prototype.some = function (fn, context) {
    	var passed = false;
    	if (typeof fn === "function") {
       	  for (var k = 0, length = this.length; k < length; k++) {
    		  if (passed === true) break;
    		  passed = !!fn.call(context, this[k], k, this);
    	  }
        }
    	return passed;
      };
    }

    因而,咱們就有了「朕準了」的demo,您能夠狠狠地點擊這裏:兼容處理後的some方法demo

    IE7瀏覽器下some擴展demo結果

  5. every
    some的基友關係已是公開的祕密了,一樣是返回Boolean值,不過,every須要每個妃子都要讓朕滿意,不然——「來人,給我拖出去砍了!」

    IE6-IE8擴展(與some相比就是truefalse調換一下):

    if (typeof Array.prototype.every != "function") {
      Array.prototype.every = function (fn, context) {
        var passed = true;
        if (typeof fn === "function") {
           for (var k = 0, length = this.length; k < length; k++) {
              if (passed === false) break;
              passed = !!fn.call(context, this[k], k, this);
          }
        }
        return passed;
      };
    }

    仍是那個朕的例子,您能夠狠狠地點擊這裏:是否every妃子讓朕滿意demo

    if (scores.every(higherThanCurrent)) {
      console.log("朕準了!");
    } else {
      console.log("來人,拖出去斬了!");        
    }

    結果是:
    every方法結果

  6. indexOf
    indexOf方法在字符串中自古就有,string.indexOf(searchString, position)。數組這裏的indexOf方法與之相似。
    array.indexOf(searchElement[, fromIndex])

    返回整數索引值,若是沒有匹配(嚴格匹配),返回-1. fromIndex可選,表示從這個位置開始搜索,若缺省或格式不合要求,使用默認值0,我在FireFox下測試,發現使用字符串數值也是能夠的,例如"3"3均可以。

    var data = [2, 5, 7, 3, 5];
    
    console.log(data.indexOf(5, "x")); // 1 ("x"被忽略)
    console.log(data.indexOf(5, "3")); // 4 (從3號位開始搜索)
    
    console.log(data.indexOf(4)); // -1 (未找到)
    console.log(data.indexOf("5")); // -1 (未找到,由於5 !== "5")

    兼容處理以下:

    if (typeof Array.prototype.indexOf != "function") {
      Array.prototype.indexOf = function (searchElement, fromIndex) {
        var index = -1;
        fromIndex = fromIndex * 1 || 0;
    
        for (var k = 0, length = this.length; k < length; k++) {
          if (k >= fromIndex && this[k] === searchElement) {
              index = k;
              break;
          }
        }
        return index;
      };
    }

    一個路子下來的,顯然,輪到demo了,您能夠狠狠地點擊這裏:兼容處理後indexOf方法測試demo

    下圖爲ietester IE6下的截圖:
    IE6下indexOf擴展後測試結果截圖

  7. lastIndexOf
    lastIndexOf方法與indexOf方法相似:
    array.lastIndexOf(searchElement[, fromIndex])

    只是lastIndexOf是從字符串的末尾開始查找,而不是從開頭。還有一個不一樣就是fromIndex的默認值是array.length - 1而不是0.

    IE6等瀏覽器以下折騰:

    if (typeof Array.prototype.lastIndexOf != "function") {
      Array.prototype.lastIndexOf = function (searchElement, fromIndex) {
        var index = -1, length = this.length;
        fromIndex = fromIndex * 1 || length - 1;
    
        for (var k = length - 1; k > -1; k-=1) {
            if (k <= fromIndex && this[k] === searchElement) {
                index = k;
                break;
            }
        }
        return index;
      };
    }

    因而,則有:

    var data = [2, 5, 7, 3, 5];
    
    console.log(data.lastIndexOf(5)); // 4
    console.log(data.lastIndexOf(5, 3)); // 1 (從後往前,索引值小於3的開始搜索)
    
    console.log(data.lastIndexOf(4)); // -1 (未找到)

    懶得截圖了,結果查看可狠狠地點擊這裏:lastIndexOf測試demo

  8. reduce
    reduce是JavaScript 1.8中才引入的,中文意思爲「減小」、「約簡」。不過,從功能來看,我我的是沒法與「減小」這種含義聯繫起來的,反而更接近於「迭代」、「遞歸(recursion)」,擦,由於單詞這麼接近,不會是ECMA-262 5th制定者筆誤寫錯了吧~~

    此方法相比上面的方法都複雜,用法以下:

    array.reduce(callback[, initialValue])

    callback函數接受4個參數:以前值、當前值、索引值以及數組自己。initialValue參數可選,表示初始值。若指定,則看成最初使用的previous值;若是缺省,則使用數組的第一個元素做爲previous初始值,同時current日後排一位,相比有initialValue值少一次迭代。

    var sum = [1, 2, 3, 4].reduce(function (previous, current, index, array) {
      return previous + current;
    });
    
    console.log(sum); // 10

    說明:

    1. 由於initialValue不存在,所以一開始的previous值等於數組的第一個元素。
    2. 從而current值在第一次調用的時候就是2.
    3. 最後兩個參數爲索引值index以及數組自己array.

    如下爲循環執行過程:

    // 初始設置
    previous = initialValue = 1, current = 2
    
    // 第一次迭代
    previous = (1 + 2) =  3, current = 3
    
    // 第二次迭代
    previous = (3 + 3) =  6, current = 4
    
    // 第三次迭代
    previous = (6 + 4) =  10, current = undefined (退出)

    有了reduce,咱們能夠輕鬆實現二維數組的扁平化:

    var matrix = [
      [1, 2],
      [3, 4],
      [5, 6]
    ];
    
    // 二維數組扁平化
    var flatten = matrix.reduce(function (previous, current) {
      return previous.concat(current);
    });
    
    console.log(flatten); // [1, 2, 3, 4, 5, 6]

    兼容處理IE6-IE8:

    if (typeof Array.prototype.reduce != "function") {
      Array.prototype.reduce = function (callback, initialValue ) {
         var previous = initialValue, k = 0, length = this.length;
         if (typeof initialValue === "undefined") {
            previous = this[0];
            k = 1;
         }
         
        if (typeof callback === "function") {
          for (k; k < length; k++) {
             this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
          }
        }
        return previous;
      };
    }

    而後,測試整合,demo演示,您能夠狠狠地點擊這裏:兼容IE6的reduce方法測試demo

    IE6瀏覽器下結果以下圖:
    reduce方法測試頁面結構示意

  9. reduceRight
    reduceRightreduce相比,用法相似:
    array.reduceRight(callback[, initialValue])

    實現上差別在於reduceRight是從數組的末尾開始實現。看下面這個例子:

    var data = [1, 2, 3, 4];
    var specialDiff = data.reduceRight(function (previous, current, index) {
      if (index == 0) {
        return previous + current;
      }
      return previous - current;
    });
    
    console.log(specialDiff); // 0

    結果0是如何獲得的呢?
    咱們一步一步查看循環執行:

    // 初始設置
    index = 3, previous = initialValue = 4, current = 3
    
    // 第一次迭代
    index = 2, previous = (4- 3) = 1, current = 2
    
    // 第二次迭代
    index = 1, previous = (1 - 2) = -1, current = 1
    
    // 第三次迭代
    index = 0, previous = (-1 + 1) = 0, current = undefined (退出)

    爲使低版本瀏覽器支持此方法,您能夠添加以下代碼:

    if (typeof Array.prototype.reduceRight != "function") {
      Array.prototype.reduceRight = function (callback, initialValue ) {
        var length = this.length, k = length - 1, previous = initialValue;
        if (typeof initialValue === "undefined") {
            previous = this[length - 1];
            k--;
        }
        if (typeof callback === "function") {
           for (k; k > -1; k-=1) {          
              this.hasOwnProperty(k) && (previous = callback(previous, this[k], k, this));
           }
        }
        return previous;
      };
    }

    您能夠狠狠地點擊這裏:reduceRight簡單使用demo

    對比FireFox瀏覽器和IE7瀏覽器下的結果:
    FireFox瀏覽器下reduceRight測試結果對比IE7下reduceRight測試結果截圖 張鑫旭-鑫空間-鑫生活

更進一步的應用

咱們還能夠將上面這些數組方法應用在其餘對象上。

例如,咱們使用forEach遍歷DOM元素。

var eleDivs = document.getElementsByTagName("div");
Array.prototype.forEach.call(eleDivs, function(div) {
    console.log("該div類名是:" + (div.className || "空"));
});

能夠輸出頁面全部div的類名,您能夠狠狠地點擊這裏:Array新方法forEach遍歷DOM demo

結果以下,IE6下,demo結果:
forEach方法遍歷DOM的使用演示 張鑫旭-鑫空間-鑫生活

等不少其餘類數組應用。

最後一點點了

本文爲低版本IE擴展的Array方法我都合併到一個JS中了,您能夠輕輕的右鍵這裏或下載或查看:es5-array.js

以上全部未IE擴展的方法都是本身根據理解寫的,雖然多番測試,不免還會有細節遺漏的,歡迎指出來。

參考文章:
JavaScript array 「extras」 in detail
Array.forEach

關於數組方法的簡單說明,也可參看這篇文章

相關文章
相關標籤/搜索