ES5中新增的很多東西,瞭解之對咱們寫JavaScript會有很多幫助,好比數組這塊,咱們可能就不須要去有板有眼地for
循環了。javascript
ES5中新增了寫數組方法,以下:html
瀏覽器支持java
對於讓人失望不少次的IE6-IE8瀏覽器,Array原型擴展能夠實現以上所有功能,例如forEach
方法:數組
// 對於古董瀏覽器,如IE6-IE8
if (typeof Array.prototype.forEach != "function") { Array.prototype.forEach = function () { /* 實現 */ }; }
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
所以,咱們有:ide
[].forEach(function(value, index, array) {
// ...
});
對比jQuery中的$.each
方法:wordpress
$.each([], function(index, value, array) {
// ...
});
會發現,第1個和第2個參數正好是相反的,你們要注意了,不要記錯了。後面相似的方法,例如$.map
也是如此。函數
如今,咱們就可使用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瀏覽器下:
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
:
在實際使用的時候,咱們能夠利用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瀏覽器:
filter
爲「過濾」、「篩選」之意。指數組filter
後,返回過濾後的新數組。用法跟map
極爲類似:
array.filter(callback,[ thisObject]);
filter
的callback
函數須要返回布爾值true
或false
. 若是爲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
實際上,存在一些語法糖能夠實現
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]
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
some
的基友關係已是公開的祕密了,一樣是返回Boolean值,不過,every
須要每個妃子都要讓朕滿意,不然——「來人,給我拖出去砍了!」
IE6-IE8擴展(與some
相比就是true
和false
調換一下):
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("來人,拖出去斬了!"); }
結果是:
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下的截圖:
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
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
說明:
initialValue
不存在,所以一開始的previous
值等於數組的第一個元素。current
值在第一次調用的時候就是2
.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瀏覽器下結果以下圖:
reduceRight
跟reduce
相比,用法相似:
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瀏覽器下的結果:
咱們還能夠將上面這些數組方法應用在其餘對象上。
例如,咱們使用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結果:
等不少其餘類數組應用。
本文爲低版本IE擴展的Array方法我都合併到一個JS中了,您能夠輕輕的右鍵這裏或下載或查看:es5-array.js
以上全部未IE擴展的方法都是本身根據理解寫的,雖然多番測試,不免還會有細節遺漏的,歡迎指出來。
參考文章:
JavaScript array 「extras」 in detail
Array.forEach
原創文章,轉載請註明來自張鑫旭-鑫空間-鑫生活[http://www.zhangxinxu.com]
本文地址:http://www.zhangxinxu.com/wordpress/?p=3220