不知道從什么時(shí)候開(kāi)始,for循環(huán)成為了菜鳥(niǎo)標(biāo)配。在《javascript高級(jí)編程中》,今天主要總結(jié)幾個(gè)常常在高手們代碼中看到的幾個(gè)ES5的方法郑什。
對(duì)于前端的循環(huán)遍歷我們知道有
- 針對(duì)js數(shù)組的forEach()、map()蒲肋、filter()、reduce()方法
- 針對(duì)js對(duì)象的for/in語(yǔ)句(for/in也能遍歷數(shù)組钝满,但不推薦)
- 針對(duì)jq數(shù)組/對(duì)象的$.each()方法
在語(yǔ)法和參數(shù)上他們有什么不同呢兜粘?
1.forEach: array.forEach(function(currentValue,index,arr), thisValue)
2.map: array.map(function(currentValue,index,arr), thisValue)
3.filter: array.filter(function(currentValue,index,arr), thisValue)
4.reduce: array.reduce(function(total,currentValue,index,arr), thisValue)
5.$.each: $.each( object/array, function(index,elment) );//jQuery的遍歷方法,這里先不多說(shuō)
6.for/in: for (var key in object) { //... }
這些方法都是源于for的封裝而來(lái)的弯蚜,先來(lái)看看for是怎么循環(huán)一個(gè)數(shù)組的
var arr = [4,3,2,1];
var index = [];
var value = [];
var sum = 0;
for(var i=0;i<arr.length;i++){
index.push(i);
value.push(arr[i])
sum += arr[i]
};
console.log(index); //[0, 1, 2, 3]
console.log(value); // [4,3,2,1]
console.log(sum); //10
//可以看出孔轴,i表示的是數(shù)組下標(biāo),arr[i]是通過(guò)下標(biāo)來(lái)去的對(duì)應(yīng)的值
forEach碎捺、map路鹰、filter、reduce方法相同點(diǎn)
**參數(shù)
既然他們參數(shù)都是一樣的诵叁,我們以forEach()求和為例雁竞,看看各個(gè)參數(shù)代表著什么
var arr = [4,3,2,1];
var sum = 0;
arr.forEach(function(val,index,arr){
console.log(val); //4
console.log(index); //0
console.log(arr); //[4,3,2,1]
console.log(arr[index]==val); // ==> true
sum+=val
});
console.log(sum); //10
從上可得,這幾個(gè)方法中參數(shù)所代表的都是相同的拧额。
關(guān)于參數(shù)還有一個(gè)點(diǎn)沒(méi)說(shuō)的是碑诉,reduce方法還有個(gè)參數(shù),語(yǔ)法如下:
array.reduce(function(total, currentValue, index, arr), initialValue)
其中 currentValue, index, arr意義相同侥锦,而total代表計(jì)算的初始值, 也是計(jì)算結(jié)束后的返回值进栽。
其中total, currentValue都是必須的參數(shù)。
對(duì)于計(jì)算一個(gè)數(shù)組的和恭垦,reduce就是很好的方法
var arr = [4,3,2.1,1.1];
var sum = arr.reduce(function(total, val) {
return total + Math.round(val);
});
console.log(sum);//10
**迭代時(shí)不做修改
這些方法處理數(shù)組時(shí)快毛,數(shù)組元素的范圍是在 callback 方法第一次調(diào)用之前就已經(jīng)確定了格嗅。;若已經(jīng)存在的元素被改變或刪除了祸泪,則它們的傳遞到 callback 的值是 該方法遍歷到它們的那一時(shí)刻的值吗浩;被刪除的元素將不會(huì)被訪問(wèn)到。例如:
var words = ["one", "two", "three", "four"];
words.forEach(function(word) {
console.log(word);
if (word === "two") {
words.shift();
}
});
console.log(words);//["two", "three", "four"]
**兼容舊環(huán)境
這些方法都是ECMA5新增的數(shù)組方法没隘,所以ie9以下都不支持懂扼,不過(guò),可以從Array原型拓展從而實(shí)現(xiàn)以上全部功能右蒲,例如forEach方法:
if (typeof Array.prototype.forEach != "function") {
Array.prototype.forEach = function() {
/* 實(shí)現(xiàn) */
};
}
下面來(lái)看看這幾個(gè)方法不同的地方
定義:
- forEach() 方法用于調(diào)用數(shù)組的每個(gè)元素阀湿,并將元素傳遞給回調(diào)函數(shù)。
- map() 方法返回一個(gè)新數(shù)組瑰妄,數(shù)組中的元素為原始數(shù)組元素調(diào)用函數(shù)處理后的值陷嘴。
map()方法按照原始數(shù)組元素順序依次處理元素 - filter() 方法創(chuàng)建一個(gè)新的數(shù)組,新數(shù)組中的元素是通過(guò)檢查指定數(shù)組中符合條件的所有元素间坐。沒(méi)有到?jīng)]有符合條件時(shí)返回空數(shù)組灾挨。
- reduce() 方法接收一個(gè)函數(shù)作為累加器,數(shù)組中的每個(gè)值(從左到右)開(kāi)始縮減竹宋,最終計(jì)算為一個(gè)值
forEach | map | filter | reduce | |
---|---|---|---|---|
操作 | 循環(huán)(迭代) | 映射 | 過(guò)濾器 | 匯總 |
返回值 | undefined | 返回新數(shù)組 | 返回新數(shù)組 | 返回計(jì)算結(jié)果total |
改變?cè)瓟?shù)組劳澄? | 看情況 | 否 | 否 | 否 |
檢測(cè)空數(shù)組? | 不檢測(cè) | 不檢測(cè) | 不檢測(cè) | 不檢測(cè) |
下面來(lái)看看這幾個(gè)方法在應(yīng)用中的不同:
1.對(duì)當(dāng)前數(shù)組每個(gè)元素乘于100
1.for方法
var b = [1,2,3];
var f = [];
for(var i=0;i<b.length;i++){
f.push(b[i]*100)
};
console.log(f); //[100, 200, 300]
2.forEach方法:
var b = [1,2,3];
var f = []
b.forEach(function(v){
f.push(v*100)
});
console.log(f); //[100, 200, 300]
console.log(b); // [1, 2, 3]
2. forEach方法:
var b = [1,2,3];
b.forEach(function(item,index,arr){
arr[index] = item*100;
});
console.log(b); //[100, 200, 300]
3.map方法:
var b = [1,2,3];
var c = b.map(function(v){ return v*100} )
console.log(c); //[100, 200, 300]
4.for/in語(yǔ)句
var b = [1,2,3];
var f = [];
for(var k in b){
f.push(b[k]*100)
}
console.log(f); //[100, 200, 300]
2.對(duì)數(shù)組的求和
1.for循環(huán)
var arr = [1,2,3,4,5];
var sum = 0; //這里sum設(shè)置為0或null
for(i=0;i<arr.length;i++){
sum += arr[i];
};
console.log(sum);//15
2.forEach方法
var arr = [1,2,3,4,5];
var sum = 0;
arr.forEach(function(v){
sum += v
})
console.log(sum);//15
3.map方法
//map不適合用來(lái)做和蜈七,因?yàn)樗菍?duì)每個(gè)元素進(jìn)行處理秒拔,再返回每個(gè)元素
4.for/in語(yǔ)句
var arr = [1,2,3,4,5];
var sum = 0;
for(var k in arr){
sum += arr[k]
};
console.log(sum); //15
3.js如何獲取json對(duì)象數(shù)組中某個(gè)屬性結(jié)合?
var arr = [
{a:1 ,b:2 ,c:3},
{a:4 ,b:5 ,c:6},
{a:7 ,b:8 ,c:9}
];
獲取數(shù)組arr的a屬性集合飒硅,有哪些方法?
1.for循環(huán)
var res = [];
for(var i=0;i<arr.length;i++){
res.push(arr[i].a)
};
console.log(res); // [1, 4, 7]
2.forEach方法
var res3 = [];
arr.forEach(function(v){
res3.push(v.a);
});
console.log(res3); // [1, 4, 7]
3.map方法
var res2 = arr.map(function(v){
return v.a
});
console.log(res2); // [1, 4, 7]
4.for/in語(yǔ)句
var res4 = [];
for(var k in arr){
res4.push(k);
};
console.log(res4); // ["0", "1", "2"]
//for in 原本是遍歷對(duì)象的砂缩,k為屬性的鍵,所以k在這里為數(shù)組的下標(biāo)三娩。應(yīng)改成如下
console.log('-----------------------');
var res5 = [];
for(k in arr){
res5.push(arr[k].a)
};
console.log(res5)庵芭; //[1, 4, 7]
4.給json對(duì)象數(shù)組中的每個(gè)對(duì)象多加個(gè)字段
var users = [
{
lastName: 'Li',
firstName: 'Lei'
},
{
lastName: 'Han',
firstName: 'Meimei'
}
];
給其中每一個(gè)對(duì)象加一個(gè)fullName字段,就把lastName和firstName
1.for循環(huán)
for(var i = 0; i < users.length; i++){
var user = users[i];
user.fullName = user.lastName + user.firstName;
}
代碼是對(duì)的尽棕,但卻不好(優(yōu)秀)喳挑,為什么?原因有2
創(chuàng)建了與主業(yè)務(wù)無(wú)關(guān)的for loop
創(chuàng)建了與主業(yè)務(wù)無(wú)關(guān)的變量i
用forEach的好處是什么滔悉?答案就是解決了上面那2個(gè)缺陷伊诵,代碼如下:
2.forEach方法
users.forEach(function(user, index, arr){
user.fullName = user.lastName + user.firstName;
});
3.map方法
var newUsers = users.map(function(v,i,arr){
v.fullName = v.lastName+v.firstName;
return v
});
//主要如果這里return v.fullName = v.lastName+v.firstName;的話,得到的是["LiLei", "HanMeimei"]
//注意: 此處的map會(huì)改變?cè)紨?shù)組回官,因?yàn)榻ov多加了個(gè)屬性v.fullName
從上我們可以看出曹宴,forEach,for/in歉提,map都是封裝了for循環(huán)笛坦,只是在應(yīng)用的對(duì)象上稍有些不同区转,例如,
forEach主要數(shù)組的一些簡(jiǎn)單遍歷
map主要是對(duì)數(shù)內(nèi)每個(gè)元素的操作
for/in主要是對(duì)象鍵值的一些遍歷
上面的分析和舉例版扩,同一種功能不同方法的實(shí)現(xiàn)废离,主要是為了讓大家理解每個(gè)方法的實(shí)現(xiàn)的內(nèi)在原理(for循環(huán)),被封裝后有什么差異礁芦,比如不同參數(shù)的值蜻韭,返回結(jié)果,以便在以后的實(shí)際應(yīng)用中能根據(jù)業(yè)務(wù)需求用更簡(jiǎn)便合適的方法來(lái)實(shí)現(xiàn)柿扣。
應(yīng)用與細(xì)節(jié)
forEach方法
forEach的應(yīng)用只要是數(shù)組的簡(jiǎn)單遍歷肖方,這里就不在多做闡述
map方法
map()對(duì)數(shù)組的每個(gè)元素進(jìn)行一定的操作(映射)后,會(huì)返回一個(gè)新的數(shù)組;是處理服務(wù)器返回信息非常有用的函數(shù)未状。
- 求數(shù)組中每個(gè)元素的平方↓
只有一個(gè)參數(shù)來(lái)mapping一個(gè)數(shù)字?jǐn)?shù)組
var res = [1,4,9].map(function(val){
return val*2 //[2,8,18]
});
- 求數(shù)組中每個(gè)元素的平方根↓
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
//roots的值為[1, 2, 3]
//numbers的值仍為[1, 4, 9]
- 使用map獲取json數(shù)組中的某個(gè)屬性集合
var users = [
{name:'zhou' ,email:'zhou@email.com'},
{name:'lin' ,email:'lin@email.com'},
{name:'wu' ,email:'wu@email.com'}
];
var emails = users.map(function(v){
return v.email
});
console.log(emails)
// ["zhou@email.com", "lin@email.com", "wu@email.com"]
- 使用map重新格式化對(duì)象數(shù)組中的對(duì)象↓
var arr= [
{key: 1, value: 10},
{key: 2, value: 20},
{key: 3, value: 30}
];
var reformattedArray = arr.map(function(obj) {
var rObj = {};
rObj[obj.key] = obj.value;
return rObj; //[{1: 10}, {2: 20}, {3: 30}]
});
//注意:
這里是return rObj整個(gè)對(duì)象俯画,
如果是return rObj[obj.key] = obj.value; 值為[10,20,30]
可以看出以上map()的用法都是對(duì)集合里的每個(gè)元素做對(duì)應(yīng)的實(shí)際的操作后,再返回到新的數(shù)組里司草。那如何使只對(duì)集合的某些元素做判斷呢艰垂?返回的是什么,如下面例子:
在數(shù)組中取大于3的全部元素
[2, 3, 4, 5].map(function(val, key) {
return val > 3; //[false, false, true, true]
})
[2, 3, 4, 5].map(function(val, key) {
if(val > 3){ return val} //[undefined, undefined, 4, 5]
})
上面的結(jié)果都不是我們想要的埋虹,我們想要的只是純粹的大于3的集合[4,5]材泄,對(duì)集合的每個(gè)元素進(jìn)行判斷,刷選出符合條件的元素吨岭,該怎么做呢?filter就是專(zhuān)為這種處理而生的峦树。
filter方法
filter方法主要是對(duì)數(shù)組的篩選過(guò)濾辣辫,返回符合條件的元素,
例如魁巩,
------ 對(duì)于數(shù)組
// 篩選出大于3的數(shù)
[2, 3, 4, 5,10].filter(function(val, index) {
return val > 3; //[4,5]
})
// 篩選出能整除5的數(shù)
[2, 3, 4, 5,10].filter(function(val, index) {
return val % 5 == 0; //[5,10]
})
----- 對(duì)于json數(shù)組
篩選對(duì)象數(shù)組中含有‘orange’屬性值的對(duì)象
var arr = [
{"name":"apple", "count": 2},
{"name":"orange", "count": 5},
{"name":"pear", "count": 3},
{"name":"orange", "count": 16},
];
1.filter方法
var newArr = arr.filter(function(item){
return item.name === "orange";
});
console.log(newArr);//
[{"name":"orange", "count": 5},
{"name":"orange", "count": 16}]
2.forEach方法
var newArr2 = [];
arr.forEach(function(v){
if(v.name === 'orange'){
newArr2.push(v)
}
});
console.log(newArr2);//
[{"name":"orange", "count": 5},
{"name":"orange", "count": 16}]
4.for循環(huán)
var newArr4 = [];
for(var i= 0, l = arr.length; i< l; i++){
if(arr[i].name === "orange" ){
newArr4.push(arr[i]);
}
}
console.log(newArr4); //
[{"name":"orange", "count": 5},
{"name":"orange", "count": 16}]
3.map方法
var newArr3 = arr.map(function(item){
return item.name === "orange";
});
console.log(newArr3);
//[false, true, false, true]
Console.log(‘-------------------------------’)
var newArr3 = arr.map(function(v){
if(v.name === 'orange'){ return v }
});
console.log(newArr3)
//
[
undefined,
{"name":"orange", "count": 5},
{"name":"orange", "count": 16},
undefined
]
reduce方法
語(yǔ)法:
arr.reduce(function(prev,cur,index,arr){
// do sth
}, init);
參數(shù):
- prev 表示上一次調(diào)用回調(diào)時(shí)的返回值急灭,或者初始值 init;
- cur 表示當(dāng)前正在處理的數(shù)組元素;
- index 表示當(dāng)前正在處理的數(shù)組元素的索引谷遂,若提供 init 值葬馋,則索引為0,否則索引為1肾扰;
- arr 表示原數(shù)組畴嘶;
- init 表示初始值。
案例:
- 求數(shù)組項(xiàng)之和
var arr = [3,9,4,3,6,0,9]
var sum = arr.reduce(function (prev, cur) {
return prev + cur;
},0)
由于傳入了初始值0集晚,所以開(kāi)始時(shí)prev的值為0窗悯,cur的值為數(shù)組第一項(xiàng)3,相加之后返回值為3作為下一輪回調(diào)的prev值偷拔,然后再繼續(xù)與下一個(gè)數(shù)組項(xiàng)相加蒋院,以此類(lèi)推亏钩,直至完成所有數(shù)組項(xiàng)的和并返回。
- 求數(shù)組項(xiàng)最大值
var arr = [3,9,4,3,6,0,9]
var max = arr.reduce(function (prev, cur) {
return Math.max(prev,cur);
})
由于未傳入初始值欺旧,所以開(kāi)始時(shí)prev的值為數(shù)組第一項(xiàng)3姑丑,cur的值為數(shù)組第二項(xiàng)9,取兩值最大值后繼續(xù)進(jìn)入下一輪回調(diào)辞友。
- 數(shù)組去重
var arr = [3,9,4,3,6,0,9]
var newArr = arr.reduce(function (prev, cur) {
prev.indexOf(cur) === -1 && prev.push(cur);
return prev;
},[])
4)將二維數(shù)組轉(zhuǎn)換成一維數(shù)組
var arr = [[1,2,3],[4,5,6],[6,7,8]]
let flat = arr.reduce(function(prev,next){
return prev.concat(next)
})
應(yīng)用
根據(jù)實(shí)際需求做合適的數(shù)據(jù)處理
有一組成績(jī)栅哀,需做一些操作:
1 輸出全部考生名字
1 成績(jī)大于60記為及格,否則不及格
2 過(guò)濾出成績(jī)大于60的數(shù)據(jù)
3 計(jì)算出總成績(jī)
4 輸出:"姓名:xx 成績(jī):1xx"格式
var grades= [
{name: "優(yōu)優(yōu)", grade: 92},
{name: "小渣", grade: 55},
{name: "小優(yōu)", grade: 82}
]
grades.map(v => v.name)
// ["優(yōu)優(yōu)", "小渣", "小優(yōu)"]
grades.map(v => v.grade > 60 ? '及格' : '不及格')
// ["及格", "不及格", "及格"]
grades.filter(v => v.grade > 60)
// [{name: "優(yōu)優(yōu)", grade: 92}, {name: "小優(yōu)", grade: 82}]
grades.reduce((total, v, i, arr) => {
return total + v.grade
},0)
//229
grades.forEach((v, i, arr) => {
v.all = `姓名:${v.name}踏枣, 成績(jī):${v.grade}`
});
console.log(grades)
//[{name: "優(yōu)優(yōu)", grade: 92, all: "姓名:優(yōu)優(yōu)昌屉, 成績(jī):92"},
//{name: "小渣", grade: 55, all: "姓名:小渣, 成績(jī):55"},
//{name: "小優(yōu)", grade: 82, all: "姓名:小優(yōu)茵瀑, 成績(jī):82"}]
//forEach方法會(huì)改變?cè)紨?shù)組