ES5中新增的Array方法详细说明
一、前言-索引
ES5中新增的不少東西,了解之對我們寫JavaScript會有不少幫助,比如數組這塊,我們可能就不需要去有板有眼地for循環了。
ES5中新增了寫數組方法,如下:
瀏覽器支持
- Opera 11+
- Firefox 3.6+
- Safari 5+
- Chrome 8+
- Internet Explorer 9+
對于讓人失望很多次的IE6-IE8瀏覽器,Array原型擴展可以實現以上全部功能,例如forEach方法:
// 對于古董瀏覽器,如IE6-IE8if (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類型,默認有傳參,這些參數分別是?見下面:
[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個是數組本身。
因此,我們有:
[].forEach(function(value, index, array) {// ... });對比jQuery中的$.each方法:
$.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-IE8if (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方法
例如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, 16callback需要有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.comArray.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說明:
以下為循環執行過程:
// 初始設置 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是如何得到的呢?
我們一步一步查看循環執行:
為使低版本瀏覽器支持此方法,您可以添加如下代碼:
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
轉載于:https://www.cnblogs.com/vsmart/p/6401716.html
總結
以上是生活随笔為你收集整理的ES5中新增的Array方法详细说明的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TypeScript和JavaScrip
- 下一篇: STM32 的加密实现(转)