人生不過一場絢爛循環

寫在前面

在某個祕密的季節,青春逝去,不復重來。或許它只是殘留在清澈陽光下的某一個不經意的思緒碎片,但它依然能讓咱們的眼睛裏泛出淡淡的光來。javascript

額,很差意思,走錯片場了,今日不談李易安,且論循環。html

本文講的是js的循環遍歷並不是事件循環機制,立意不深,大神蜻蜓點水,衆生可搶沙發。java

一丶原生的JS循環

1.1 while 循環

while語句包括一個循環條件和一段代碼塊,只要條件爲真,就不斷循環執行代碼塊。先判斷後執行es6

var num = 1;//1.聲明循環變量
while (num<=10){//2.判斷循環條件
	document.write(num+" ");//3.執行循環體
	num++;//4.跟新循環變量
}
複製代碼

1.2 do...while 循環

do...while循環與while循環相似,惟一的區別就是先運行一次循環體,而後判斷循環條件。即先執行後判斷json

var num = 10;
do{
	document.write(num+" ");
	num--;
}while(num>=0);
document.write(num);//-1
複製代碼

1.3 for循環

for循環有三個表達式:①聲明循環變量;②判斷循環條件;③更新循環變量;先判斷後執行,與while相同。數組

for(var num=0;num<10;num++){
	document.write(num+" ");
}
複製代碼

1.4 for...in 循環

for...in 循環主要用於遍歷對象。bash

var obj = {a:1,b:2,c:3};
for(var i in obj){
	console.log('鍵名:'+i);
	console.log('鍵值:'+obj[i]);
}
複製代碼

須要注意點:

for...in 循環,遍歷時不只能讀取對象自身上面的成員屬性,也能延續原型鏈遍歷出對象的原型屬性,可使用hasOwnProperty判斷一個屬性是否是對象自身上的屬性。obj.hasOwnProperty(keys)==true 表示這個屬性是對象的成員屬性,而不是原型屬性。數據結構

var person = {name:'小夏'};
for(var key in person){
	if(person.hasOwnProperty(key)){
		console.log(key);//name
	}
}
複製代碼

再聊一點:遍歷json對象app

無規律json數組:函數

var json = [{qwer:'姚姚',a:'is',b:1}, {ob:'da',tp:'sb'}];
 
for(var i=0,l=json.length;i<l;i++){//此處長度用變量保存下來可節約資源的消耗
    for(var key in json[i]){
        alert(key+':'+json[i][key]);
    }
}
複製代碼

有規律json數組:

var packJson = [
    {"name": "nikita", "password": "1111"},
    {"name": "tony", "password": "2222"}
];
 
for (var p in packJson) {//遍歷json數組時,這麼寫p爲索引,0,1
    alert(packJson[p].name + " " + packJson[p].password);
}
複製代碼

1.5 for...of 循環

ES6 借鑑 C++、Java、C# 和 Python 語言,引入了for...of循環,做爲遍歷全部數據結構的統一的方法。

一個數據結構只要部署了Symbol.iterator屬性,就被視爲具備iterator接口,就能夠用for...of循環遍歷它的成員。也就是說,for...of循環內部調用的是數據結構的Symbol.iterator方法。for...of循環可使用的範圍包括數組、Set和Map結構、某些相似數組的對象(好比arguments對象、DOM NodeList 對象)、後文的 Generator 對象,以及字符串。

下面咱們對一些數據結構進行遍歷:

1.5.1 數組

var arr = ['a','b','c','d'];
for(let i in arr){
	console.log(i);//0 1 2 3
}
for(let j of arr){
	console.log(j);//a b c d
}
var iterator = arr.entries();
for(let j of iterator){
	console.log(j);//[0,"a"] [1,"b"] [2,"c"] [3,"d"]
}
var iterator = arr.keys();
console.log(iterator);
for(let j of iterator){
	console.log(j);//0 1 2 3
}
複製代碼

上面代碼代表,for...in循環讀取鍵名,for...of循環讀取鍵值。若是要經過for...of循環,獲取數組的索引,能夠藉助數組實例的entries方法和keys方法。

1.5.2 Set和Map結構

//Set 結構
var newSet = new Set(["a","b","c","c"]);
for(var e of newSet){
   console.log(e);//a b c
}

// Map結構
var es6 = new Map();
console.log(es6);
es6.set('yaoyao',1);
es6.set('bubu','2');
es6.set('fengfeng','last');
for(var [name,value] of es6){//用name和value接受鍵名和鍵值
   console.log(name+':'+value);//yaoyao:1 bubu:2 fengfeng:last
}
複製代碼

上面代碼演示瞭如何遍歷 Set結構和Map結構。值得注意的地方有兩個,首先,遍歷的順序是按照各個成員被添加進數據結構的順序。其次,Set結構遍歷時,返回的是一個值,而Map結構遍歷時,返回的是一個數組,該數組的兩個成員分別爲當前Map成員的鍵名和鍵值。

1.5.3 相似數組的對象

相似數組的對象包括好幾類。下面是for...of循環用於字符串、DOM NodeList 對象、arguments對象的例子。

// 字符串
var str = 'GREAT';
for(let s of str){
   console.log(s);//G R E A T
}
// DOM NodeList對象
let paras = document.querySelectorAll('p');
for(let p of paras){
   console.log(p);//打印出全部p標籤
   p.classList.add('addClass');
}
// arguments對象
function printArgs(){
   for(let x of arguments){
   	console.log(x);
   }
}
printArgs([1,2],"1",{"a":3});//[1,2] "1" {"a":3}
複製代碼

1.6 Map()循環

map方法將數組的全部成員依次傳入參數函數,而後把每一次的執行結果組成一個新數組返回,而且不會改變原數組。

var numbers = [1,2,3];
var newNumbers = numbers.map(function(i){
   return i+1;
})
console.log(newNumbers);//[2,3,4]
console.log(numbers);//[1,2,3]
複製代碼

map方法接受一個函數做爲參數,該函數調用時,map方法向它傳入三個參數:當前值,當前下標和數組自己。

var newMap = [2,3,4].map(function(item,index,arr){
   return item*index;
})
console.log(newMap);//[0,3,8]
複製代碼

此外,map方法還能夠接受第二個參數,用來綁定回調函數內部的this變量,將回調函數內部的this對象,指向第二個參數,間接操做這個參數(通常是數組)。

var arr = ['a','b','c','d'];
var newArgs = [1,2].map(function(i){
   return this[i];
},arr);
console.log(newArgs);//["b","c"]
複製代碼

上面代碼經過map方法的第二個參數,將回調函數內部的this對象,指向arr數組。間接操做了數組arr;接下來說的forEach一樣具備這個功能。

1.7 forEach()循環

forEach方法與map方法很類似,也是對數組的全部成員依次執行參數函數。可是,forEach方法不返回值,只用來操做數據。也就是說,若是數組遍歷的目的是爲了獲得返回值,那麼使用map方法,不然使用forEach方法。forEach的用法與map方法一致,參數是一個函數,該函數一樣接受三個參數:當前值、當前下標、數組自己。

[4,5,6].forEach(function(ele,index,arr){
	console.log(index+':'+ele);//0:4 1:5 2:5
})
複製代碼

此外,forEach循環和map循環同樣也能夠用綁定回調函數內部的this變量,間接操做其它變量(參考上面的map()循環例子)。

1.8 filter()過濾循環

filter方法用於過濾數組成員,知足條件的成員組成一個新數組返回。它的參數是一個函數,全部數組成員依次執行該函數,返回結果爲true的成員組成一個新數組返回。該方法不會改變原數組。

var newFilter = [1,2,3,4,5,6].filter(function(i){
	return i>2
})
console.log(newFilter);//[3,4,5,6]

var arr = [0,1,'a',false,null,undefined];
var arrFilter = arr.filter(Boolean);
console.log(arrFilter);//[1,"a"]
複製代碼

看起來和Map()循環沒什麼區別,那麼比較一下能夠發現map方法內部使用判斷語句時知足返回true,不知足返回false。

var newFilter = [1,2,3,4,5,6].map(function(i){
	return i>2
})
console.log(newFilter);//[false, false, true, true, true, true]
複製代碼

filter方法的參數函數也能夠接受三個參數:當前值,當前下標和數組自己。

var allFilter = [1,2,3,4,5].filter(function(ele,index,arr){
	return index % 2 === 0;
})
console.log(allFilter);//[1,3,5]
複製代碼

filter方法也能夠接受第二個參數,用來綁定參數函數內部的this變量。

var obj = {MAX:3};
var myFilter = function(item){
	return item > this.MAX;
}
var arr = [2,8,3,4,1,3,2,9];
var newFilter = arr.filter(myFilter,obj);
console.log(newFilter);//[8,4,9]
複製代碼

1.9 some()、every()循環遍歷,統計數組是否知足某個條件

這兩個方法相似「斷言」(assert),返回一個布爾值,表示判斷數組成員是否符合某種條件。它們接受一個函數做爲參數,全部數組成員依次執行該函數。該函數接受三個參數:當前值、當前下標和數組自己,而後返回一個布爾值。

1.9.1 some()

只要有一個成員知足條件,則總體返回true;反言之,沒有任何一個成員知足條件,則總體返回false。

var arr = [1,2,3,4,5];
var newSome = arr.some(function(i){
	return i >= 3;
})
console.log(newSome);//true
複製代碼

1.9.2 every()

全部成員都知足條件,則總體返回true;反言之,只要有一個成員不知足條件,則總體返回false。

var arr = [1,2,3,4,5];
var newEvery = arr.every(function(i){
	return i>=3;
})
console.log(newEvery);//false
複製代碼

1.10 reduce()和reduceRight()

1.10.1 reduce()

reduce()方法接收一個函數做爲累加器,數組中的每一個值(從左到右)開始縮減,最終計算爲一個值。

[1, 2, 3, 4, 5].reduce(function (a, b) {
  console.log(a, b);
  return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最後結果:15
複製代碼

場景: 統計一個數組中有多少個不重複的單詞

使用for循環

var arr = ['apple','orange','apple','orange','pear','orange'];
function getWordCnt(){
	var obj = {};
	for(var i=0,l=arr.length;i<l;i++){
		var item = arr[i];
		obj[item] = (obj[item]+1)||1;
		//第一次遍歷obj.apple是undefined,因此obj.apple取1;
		//第二次遍歷obj.orange是undefined,因此obj.orange取1;
		//第三次遍歷obj.apple已是1了,因此obj.apple = 1+1;······
	}
	return obj;
}
console.log(getWordCnt());//{apple: 2, orange: 3, pear: 1}
複製代碼

reduce(callback, initialValue)會傳入兩個變量。回調函數(callback)和初始值(initialValue)。假設函數它有個傳入參數,prev和next,index和array。prev和next是必填項,index和array是選填項。通常來說prev是從數組中第一個元素開始的,next是第二個元素。可是當你傳入初始值(initialValue)後,第一個prev將是initivalValue,next將是數組中的第一個元素。

var arr = ['apple','orange','apple'];
function noPassValue(){
	return arr.reduce(function(prev,next){
		console.log('prev:',prev);
		console.log('next:',next);
		return prev+' '+next;
	})
}
console.log(noPassValue());
console.log('------------------------------');
function passValue(){
	return arr.reduce(function(prev,next){
		console.log('prev:',prev);
		console.log('next:',next);
		prev[next] = (prev[next]+1)||1;
		return prev;
	},{})
}
console.log(passValue());
複製代碼

打印結果爲:

觀察打印結果不只能夠明顯發現兩者區別,還能夠發現函數passValue做用能夠等同於上面的for循環。

1.10.2 reduceRight()

reduceRight的語法以及回調函數的規則和reduce方法是同樣的,區別就是在與reduce是升序,即角標從0開始,而reduceRight是降序,即角標從arr.length-1開始。

反轉字符串:

var word = 'ilovexx';
function AppendToArray(previousValue,currentValue){
	return previousValue + currentValue;
}
var result = [].reduceRight.call(word,AppendToArray,"the ");
console.log(result);//the xxevoli
複製代碼

1.11 Object.keys()

Object.keys方法的參數是一個對象,返回一個數組。該數組的成員都是該對象自身的(而不是繼承的)全部屬性名,且只返回可枚舉的屬性。

var obj = {
  p1: 123,
  p2: 456
};
console.log(Object.keys(obj)); // ["p1", "p2"]
複製代碼

這個方法是否是有點眼熟,不妨對比一下上面的2.5.1。

1.12 Object.getOwnPropertyNames()

Object.getOwnPropertyNames方法與Object.keys方法相似,也是接受一個對象做爲參數,返回一個數組,包含了該對象自身的全部屬性名。但它能返回不可枚舉的屬性。

var a = ['Hello', 'World'];
console.log(Object.keys(a) )// ["0", "1"]
console.log(Object.getOwnPropertyNames(a)) // ["0", "1", "length"]
複製代碼

二丶使用jQuery的遍歷

2.1 $.grep()篩選遍歷數組

grep()循環可以遍歷數組,並篩選符合條件的元素,組成新的數組,並返回。

$(function(){
    var array = [1,2,3,4,5,6,7,8,9];
    var filterarray = $.grep(array,function(value){
        return value > 5;//篩選出大於5的
    });
	console.log(filterarray);//[6,7,8,9]
    for(var i=0;i<filterarray.length;i++){
        alert(filterarray[i]);
    }
    for (key in filterarray){
        alert(filterarray[key]);
    }
})
複製代碼

2.2 $.each()篩選遍歷數組或json對象

$.each(anObject,function(name,value) {
	console.log(name);
	console.log(value);
});
var anArray = ['one','two','three'];
$.each(anArray,function(n,value){
	console.log(n);
	console.log(value);
});
複製代碼

2.3 $.inArray()篩選遍歷數組

var anArray = ['a','b','c'];
var index = $.inArray('b',anArray);
console.log(index,anArray[index]);//1 "b"
複製代碼

2.4 $.map()篩選遍歷數組

var strings = ['0','1','2','3','4','5','6'];
var values = $.map(strings,function(val){
	var result = new Number(val);
	return isNaN(result)?null:result
})
for(var key in values){
	console.log(values[key]);
}
複製代碼

jQuery的遍歷方法在平常使用中仍是比較少的,由於這幾種方法在js原生中都能找到對應的方法。寫了這麼多發現實戰中仍是for循環用的最多,哈哈哈哈哈。。。。。。不過學了這些以後起碼往後遇到循環問題有更多的解決方案,何樂不爲呢。

三丶總結——比較——看異同

其實大多數類似方法的對比在文中就已經談過了,此處將map()、forEach()、filter()三個單獨拎出來作個比較:

相同之處:

1.三種循環中途都是沒法中止的,老是會將全部成員遍歷完。

2.他們均可以接受第二個參數,用來綁定回調函數內部的this變量,將回調函數內部的this對象,指向第二個參數,間接操做這個參數(通常是數組)。

不一樣之處:

forEach()循環沒有返回值;map(),filter()循環有返回值。

四丶參考資料

你還在用for循環大法麼?
js的15種循環遍歷,你掌握了幾種?
深刻了解 JavaScript 中的 for 循環
JS中的循環---最全的循環總結

相關文章
相關標籤/搜索