、for 循環
let arr=[1,2,3]; for (let i=0; i<arr.length; i++){ console.log(i,arr[i]) } // 0 1 // 1 2 // 2 3
for 循環是 Js 中最常用的一個循環工具,經常用于數組的循環遍歷。
2、for in 循環
let obj={name:'zhou',age:'**'} for(let i in obj){ console.log(i,obj[i]) } // name zhou // age **
for in 循環主要用于遍歷普通對象,i 代表對象的 key 值,obj[i] 代表對應的 value,當用它來遍歷數組時候,多數情況下也能達到同樣的效果,但是你不要這么做,這是有風險的,因為 i 輸出為字符串形式,而不是數組需要的數字下標,這意味著在某些情況下,會發生字符串運算,導致數據錯誤,比如:'52'+1='521' 而不是我們需要的 53。
另外 for in 循環的時候,不僅遍歷自身的屬性,還會找到 prototype 上去,所以最好在循環體內加一個判斷,就用 obj[i].hasOwnProperty(i),這樣就避免遍歷出太多不需要的屬性。
3、while 循環
同樣的遍歷 cars 數組,先用 for 循環方法
let cars=["BMW","Volvo","Saab","Ford"]; let i=0; for (;cars[i];) { console.log(cars[i]) i++; }; // BMW // Volvo // Saab // Ford
然后是 while 循環方法
cars=["BMW","Volvo","Saab","Ford"]; var i=0; while (cars[i]) { console.log(cars[i] + "<br>") i++; };
我們發現,它們可以實現同樣的效果,事實上它們底層的處理是一樣的,不過 for 循環可以把定義、條件判斷、自增自減操作放到一個條件里執行,代碼看起來方便一些,僅此而已。
4、do while 循環
let i=3; do{ console.log(i) i--; } while(i>0) // 3 // 2 // 1
do while 循環是 while 循環的一個變體,它首先執行一次操作,然后才進行條件判斷,是 true 的話再繼續執行操作,是 false 的話循環結束。
5、Array forEach 循環
let arr=[1,2,3]; arr.forEach(function(i,index){ console.log(i,index) }) // 1 0 // 2 1 // 3 2
forEach循環,循環數組中每一個元素并采取操作, 沒有返回值, 可以不用知道數組長度,他有三個參數,只有第一個是必需的,代表當前下標下的 value。
另外請注意,forEach 循環在所有元素調用完畢之前是不能停止的,它沒有 break 語句,如果你必須要停止,可以嘗試 try catch 語句,就是在要強制退出的時候,拋出一個 error 給 catch 捕捉到,然后在 catch 里面 return,這樣就能中止循環了,如果你經常用這個方法,最好自定義一個這樣的 forEach 函數在你的庫里。
6、Array map()方法
let arr=[1,2,3]; let tt=arr.map(function(i){ console.log(i) return i*2; }) // [2,4,6]
map() 方法返回一個新數組,數組中的元素為原始數組元素調用函數處理后的值。
注意:map 和 forEach 方法都是只能用來遍歷數組,不能用來遍歷普通對象。
7、Array filter() 方法
let arr=[1,2,3]; let tt=arr.filter(function(i){ return i>1; }) // [2,3]
filter 方法是 Array 對象內置方法,它會返回通過過濾的元素,不改變原來的數組。
8、Array some() 方法
let arr=[1,2,3]; let tt=arr.some(function(i){ return i>1; }) // true
some() 方法用于檢測數組中的元素是否滿足指定條件(函數提供),返回 boolean 值,不改變原數組。
9、Array every() 方法
let arr=[1,2,3]; let tt=arr.some(function(i){ return i>1; }) // 檢測數組中元素是否都大于1 // false
every() 方法用于檢測數組所有元素是否都符合指定條件(通過函數提供),返回 boolean 值,不改變原數組。
10、Array reduce()方法
let arr=[1,2,3]; let ad=arr.reduce(function(i,j){ return i+j; }) // 6
reduce() 方法接收一個函數作為累加器,數組中的每個值(從左到右)開始縮減,最終計算為一個值。
11、Array reduceRight()方法
let arr=[1,2,3]; let ad=arr.reduceRight(function(i,j){ return i+j; }) // 6
reduceRight()方法,和 reduce() 功能是一樣的,它是從數組的末尾處向前開始計算。
12、for of 循環
for(let i of arr){ console.log(i) } // name // age
for of 循環是 Es6 中新增的語句,用來替代 for in 和 forEach,它允許你遍歷 Arrays(數組), Strings(字符串), Maps(映射), Sets(集合)等可迭代(Iterable data)的數據結構,注意它的兼容性。
總結
以上就是我總結的 Js 中常見的循環遍歷方法,隨著 Es6 標準的兼容性越來越好,我發現很多實現方案慢慢都不再必要了,比如 let、const 取代var 后,在某些情況下的閉包函數也就不存在了。
對前端的技術,架構技術感興趣的同學關注我的頭條號,并在后臺私信發送關鍵字:“前端”即可獲取免費的架構師學習資料
知識體系已整理好,歡迎免費領取。還有面試視頻分享可以免費獲取。關注我,可以獲得沒有的架構經驗哦!!
While語句包括一個循環條件和一段代碼塊,只要條件為真,就不斷循環執行代碼塊。
while (條件) 語句;
//舉例:
var i=0;
while (i < 100) {
console.log('i 當前為:' + i); i=i + 1;
}
do...while循環與while循環類似,唯一的區別就是先運行一次循環體,然后判斷循環條件。
do 語句 while (條件);
//舉例:
var x=3;
var i=0;
do {
console.log(i); i++;
} while(i < x);
for(var i=0;i<filterarray.length;i++){
alert(filterarray[i]);
}
var obj={a: 1, b: 2, c: 3};
for (var i in obj) {
console.log('鍵名:', i);
console.log('鍵值:', obj[i]);
}
// 鍵名: a // 鍵值: 1 // 鍵名: b // 鍵值: 2
// 其中 obj為循環的對象, i 為對象中的“鍵名”。如果對象是數組,那么i就是坐標。
注意:fo…in循環一般用于對象的遍歷,但是這里有一個坑需要注意:
任何對象都繼承了Object對象,或者其它對象,繼承的類的屬性是默認不可遍歷的,for... in循環遍歷的時候會跳過,但是這個屬性是可以更改為可以遍歷的,那么就會造成遍歷到不屬于自身的屬性。
舉例來說,對象都繼承了toString屬性,但是for...in循環不會遍歷到這個屬性。
var obj={};// toString 屬性是存在的obj.toString
// toString() { [native code] }
for (var p in obj) {
console.log(p);
} // 沒有任何輸出
如果繼承的屬性是可遍歷的,那么就會被for...in循環遍歷到。但如果只想遍歷自身的屬性,使用for...in的時候,應該結合使用hasOwnProperty方法,在循環內部判斷一下,某個屬性是否為對象自身的屬性。否則就可以產生遍歷失真的情況。
var person={ name: '老張' };
for (var key in person) {
if (person.hasOwnProperty(key)) {
console.log(key);
}
}// name
此外,for循環遍歷json對象有點奇葩:
無規律json數組:
var json=[{dd:'SB',AA:'東東',re1:123}, {cccc:'dd',lk:'1qw'}];
for(var i=0,l=json.length;i<l;i++){
for(var key in json[i]){
alert(key+’:'+json[i][key]);
}
}
為什么要 l=json.length;i<l呢?小伙伴們自己思考下吧!哈哈哈哈……
有規律json數組:
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);
}
map方法將數組的所有成員依次傳入參數函數,然后把每一次的執行結果組成一個新數組返回。
注意:是返回一個新數組,而不會改變原數組。
var numbers=[1, 2, 3];
numbers.map(function (n) {
return n + 1;
});
// [2, 3, 4]
numbers // [1, 2, 3]
map方法接受一個函數作為參數。該函數調用時,map方法向它傳入三個參數:當前成員、當前位置和數組本身。
[1, 2, 3].map(function(elem, index, arr) {
return elem * index;
});
// [0, 2, 6]
此外,map()循環還可以接受第二個參數,用來綁定回調函數內部的this變量,將回調函數內部的this對象,指向第二個參數,間接操作這個參數(一般是數組)。
var arr=['a', 'b', 'c'];
[1, 2].map(function (e) {
return this[e];
}, arr)
// ['b', 'c']
上面代碼通過map方法的第二個參數,將回調函數內部的this對象,指向arr數組。間接操作了數組arr; forEach同樣具有這個功能。
forEach方法與map方法很相似,也是對數組的所有成員依次執行參數函數。但是,forEach方法不返回值,只用來操作數據。也就是說,如果數組遍歷的目的是為了得到返回值,那么使用map方法,否則使用forEach方法。forEach的用法與map方法一致,參數是一個函數,該函數同樣接受三個參數:當前值、當前位置、整個數組。
function log(element, index, array) {
console.log('[' + index + ']=' + element);
} ;
[2, 5, 9].forEach(log); // [0]=2 // [1]=5 // [2]=9
此外,forEach循環和map循環一樣也可以用綁定回調函數內部的this變量,間接操作其它變量(參考上面的map()循環例子)。
filter方法用于過濾數組成員,滿足條件的成員組成一個新數組返回。它的參數是一個函數,所有數組成員依次執行該函數,返回結果為true的成員組成一個新數組返回。該方法不會改變原數組。
[1, 2, 3, 4, 5].filter(function (elem) {
return (elem > 3);
}) // [4, 5]
// 上面代碼將大于3的數組成員,作為一個新數組返回。
var arr=[0, 1, 'a', false];
arr.filter(Boolean) // [1, "a"]
filter方法的參數函數也可以接受三個參數:當前成員,當前位置和整個數 組。
[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
return index % 2===0;
}); // [1, 3, 5]
此外,filter方法也可以接受第二個參數,用來綁定參數函數內部的this變量。
var obj={ MAX: 3 }; var myFilter=function (item) {
if (item > this.MAX) return true;
};
var arr=[2, 8, 3, 4, 1, 3, 2, 9];
arr.filter(myFilter, obj) // [8, 4, 9]
上面代碼中,過濾器myFilter內部有this變量,它可以被filter方法的第二個參數obj綁定,返回大于3的成員。
這兩個方法類似“斷言”(assert),返回一個布爾值,表示判斷數組成員是否符合某種條件。
它們接受一個函數作為參數,所有數組成員依次執行該函數。該函數接受三個參數:當前成員、當前位置和整個數組,然后返回一個布爾值。
some方法是只要一個成員的返回值是true,則整個some方法的返回值就是true,否則返回false。
var arr=[1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
return elem >=3;
});
// true
而every方法則相反,所有成員的返回值都是true,整個every方法才返回true,否則返回false。兩相比較,some()只要有一個是true,便返回true;而every()只要有一個是false,便返回false.
var arr=[1, 2, 3, 4, 5];
arr.every(function (elem, index, arr) {
return elem >=3;
});
// false
這兩個方法在實際開發中,大有可用之處。比如在判定用戶是否勾選了不可操作的數據,或者是否勾選了一條可以操作的數據可以使用這兩個方法遍歷循環數組。
reduce方法和reduceRight方法依次處理數組的每個成員,最終累計為一個值。它們的差別是,reduce是從左到右處理(從第一個成員到最后一個成員),reduceRight則是從右到左(從最后一個成員到第一個成員),其他完全一樣。
[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
reduce方法和reduceRight方法的第一個參數都是一個函數。該函數接受以下四個參數。
累積變量,默認為數組的第一個成員
當前變量,默認為數組的第二個成員
當前位置(從0開始)
原數組
這四個參數之中,只有前兩個是必須的,后兩個則是可選的。
如果要對累積變量指定初值,可以把它放在reduce方法和reduceRight方法的第二個參數。
[1, 2, 3, 4, 5].reduce(function (a, b) {
return a + b;
}, 10);
// 25
上面的第二個參數相當于設定了默認值,處理空數組時尤其有用,可避免一些空指針異常。
由于這兩個方法會遍歷數組,所以實際上還可以用來做一些遍歷相關的操作。比如,找出字符長度最長的數組成員。
function findLongest(entries) {
return entries.reduce(function (longest, entry) {
return entry.length > longest.length ? entry : longest;
}, '');
}
findLongest(['aaa', 'bb', 'c']) // "aaa"
上面代碼中,reduce的參數函數會將字符長度較長的那個數組成員,作為累積值。這導致遍歷所有成員之后,累積值就是字符長度最長的那個成員。
Object.keys方法的參數是一個對象,返回一個數組。該數組的成員都是該對象自身的(而不是繼承的)所有屬性名,且只返回可枚舉的屬性。
var obj={
p1: 123,
p2: 456
};
Object.keys(obj) // ["p1", "p2"]
Object.getOwnPropertyNames方法與Object.keys類似,也是接受一個對象作為參數,返回一個數組,包含了該對象自身的所有屬性名。但它能返回不可枚舉的屬性。
var a=['Hello', 'World'];
Object.keys(a) // ["0", "1"]
Object.getOwnPropertyNames(a) // ["0", "1", "length"]
上面代碼中,數組的length屬性是不可枚舉的屬性,所以只出現在Object.getOwnPropertyNames方法的返回結果中。
由于 JavaScript 沒有提供計算對象屬性個數的方法,所以可以用這兩個方法代替。
var obj={
p1: 123,
p2: 456
};
Object.keys(obj).length // 2
Object.getOwnPropertyNames(obj).length // 2
1.foreach,map,filter循環中途是無法停止的,總是會將所有成員遍歷完。
2.他們都可以接受第二個參數,用來綁定回調函數內部的this變量,將回調函數內部的this對象,指向第二個參數,間接操作這個參數(一般是數組)。
forEach循環沒有返回值;map,filter循環有返回值。
var f=function (n) {
return 'a'
};
[1, undefined, 2].map(f) // ["a", "a", "a"]
[1, null, 2].map(f) // ["a", "a", "a"]
[1, , 2].map(f) // ["a", , "a"]
上面代碼中,map方法不會跳過undefined和null,但是會跳過空位。forEach方法也會跳過數組的空位,這里就不舉例了。
some()只要有一個是true,便返回true;而every()只要有一個是false,便返回false.
reduce是從左到右處理(從第一個成員到最后一個成員),reduceRight則是從右到左(從最后一個成員到第一個成員)。
他們都是遍歷對象的屬性,也是接受一個對象作為參數,返回一個數組,包含了該對象自身的所有屬性名。但Object.keys不能返回不可枚舉的屬性;Object.getOwnPropertyNames能返回不可枚舉的屬性。
grep()循環能夠遍歷數組,并篩選符合條件的元素,組成新的數組,并返回。
function(){
var array=[1,2,3,4,5,6,7,8,9];
var filterarray=$.grep(array,function(value){
return value > 5;//篩選出大于5的
});
for(var i=0;i<filterarray.length;i++){
alert(filterarray[i]);
}
for (key in filterarray){
alert(filterarray[key]);
}
}
function(){
var anObject={one:1,two:2,three:3};//對json數組each
$.each(anObject,function(name,value) {
alert(name);
alert(value);
});
var anArray=['one','two','three'];
$.each(anArray,function(n,value){
alert(n);
alert(value);
});
}
inArray()循環能返回參數在數組中對應的坐標。
function(){
var anArray=['one','two','three'];
var index=$.inArray(‘two’,anArray);
alert(index);//返回該值在數組中的鍵值,返回1
alert(anArray[index]);//value is two
}
$().ready(
function(){
var strings=['0','1','2','3','4','S','6'];
var values=$.map(strings,function(value){
var result=new Number(value);
return isNaN(result) ? null:result;//isNaN:is Not a Number的縮寫
});
for (key in values) {
alert(values[key]);
}
});
map循環常用語往數組中添加新元素,第二種寫法:
this.detEntityList.map(item=> {
//往比遍歷到的對象中添加屬性
Object.assign(item, {
sourceType: item.businessType,
})
});
map() 把每個元素通過函數傳遞到當前匹配集合中,生成包含返回值的新的 jQuery 對象。此用法與原生js的map循環用法一致。
哈哈哈哈,親愛的同學們,是不是感覺好多啊,其實不要特意去記憶啊,個人建議吧原生for循環,forEach循環,還有Juery的each熟悉就可以啦!如果你再開發者發現這幾個循環不好用的話,那就來我這里找找看,這15種循環中,絕對有你有需要的。
了方便例子講解,現有數組和字面量對象如下
var demoArr=['Javascript', 'Gulp', 'CSS3', 'Grunt', 'jQuery', 'angular'];
var demoObj={
aaa: 'Javascript',
bbb: 'Gulp',
ccc: 'CSS3',
ddd: 'Grunt',
eee: 'jQuery',
fff: 'angular'
};
可以直接看示例,用得太多了,很簡單
(function () {
for (var i=0, len=demoArr.length; i < len; i++) {
if (i==2) {
// return; // 函數執行被終止
// break; // 循環被終止
continue; // 循環被跳過
};
console.log('demo1Arr[' + i + ']:' + demo1Arr[i]);
}
})();
關于for循環,有以下幾點需要注意
var i=0, len=demo1Arr.length;
for(; i<len; i++) {};
for(var item in arr|obj){} 可以用于遍歷數組和對象
(function () {
for (var i in demoArr) {
if (i==2) {
return; // 函數執行被終止
// break; // 循環被終止
// continue; // 循環被跳過
};
console.log('demoArr[' + i + ']:' + demoArr[i]);
}
console.log('-------------');
})();
for in 本質上遍歷的是對象,之所以能遍歷數組,是因為數組也是一個對象。
var arr=['react', 'vue', 'angular'];
// 等價于
var arr={
0: 'react',
1: 'vue',
2: 'angular'
}
關于for in,有以下幾點需要注意:
function res() {
var demoArr=['Javascript', 'Gulp', 'CSS3', 'Grunt', 'jQuery', 'angular'];
for (var item in demoArr) {
if (item==2) {
return;
};
console.log(item, demoArr[item]);
}
console.log('desc', 'function res'); //不會執行
}
因為 for in 的目的是為了遍歷對象,因此在遍歷時,會同時搜索該對象構造函數上的屬性以及原型上的屬性,因此 for in 循環相對來說消耗會更大一點。因此,如果有其他更好的選擇,則盡量避免考慮使用 for in 循環來遍歷數據。
demoArr.forEach(function(arg) {})
參數arg表示數組每一項的元素,實例如下
demoArr.forEach(function (val, index) {
if (e=='CSS3') {
return; // 循環被跳過
// break; // 報錯
// continue;// 報錯
};
console.log(val, index);
})
具體有以下需要注意的地方
ES5中新增的幾個數組方法,forEach, map, filter, reduce等,可以理解為依次對數組的每一個子項進行一個處理(回調函數中的操作),他們是對簡單循環的更高一層封裝,因此與單純的循環在本質上有一些不同,所以才會導致 return, continue, break 的不同。
最重要的一點,可以添加第二參數,為一個數組,而且回調函數中的this會指向這個數組。而如果沒有第二參數,則this會指向window。
var newArr=[];
demoArr.forEach(function(val, index) {
this.push(val); // 這里的this指向newArr
}, newArr)
雖然在原生中 forEach 循環的局限性很多,但是了解他的必要性在于,很多第三方庫會擴展他的方法,使其能夠應用在很多地方,比如 angular 的工具方法中,也有 forEach 方法,其使用與原生的基本沒有差別,只是沒有了局限性,可以在IE下使用,也可以遍歷對象
var result=[];
angular.forEach(demoArr, function(val, index) {
this.push(val);
}, result);
函數具體的實現方式如下,不過有一點值得注意的是,當使用 continue時,如果你將 i++ 放在了后面,那么 i++ 的值將一直不會改變,最后陷入死循環。因此使用do/while一定要小心謹慎一點。
// 直接使用while
(function () {
var i=0,
len=demoArr.length;
while (i < len) {
if (i==2) {
// return; // 函數執行被終止
// break; // 循環被終止
// continue; // 循環將被跳過,因為后邊的代碼無法執行,i的值沒有改變,因此循環會一直卡在這里,慎用!!
};
console.log('demoArr[' + i + ']:' + demoArr[i]);
i++;
}
console.log('------------------------');
})();
// do while
(function () {
var i=0,
len=demo3Arr.length;
do {
if (i==2) {
break; // 循環被終止
};
console.log('demo2Arr[' + i + ']:' + demo3Arr[i]);
i++;
} while (i < len);
})();
不建議使用do/while的方式來遍歷數組
$.each(demoArr|demoObj, function(e, ele))
可以用來遍歷數組和對象,其中e表示索引值或者key值,ele表示value值
$.each(demoArr, function(e, ele) {
console.log(e, ele);
})
輸出為
0 "Javascript"
1 "Gulp"
2 "CSS3"
3 "Grunt"
4 "jQuery"
5 "angular"
這里有很多需要注意的地方
console.log(this);
//String {0: "C", 1: "S", 2: "S", 3: "3", length: 4, [[PrimitiveValue]]: "CSS3"}
console.log(this==ele);
// true
$.each(this, function(e, ele) {
console.log(e, ele);
})
// 0 c
// 1 s
// 2 s
// 4 3
為什么 length 和 [[PrimitiveValue]]沒有遍歷出來?突然靈光一動,在《javascript高級編程》中找到了答案,大概意思就是javascript的內部屬性中,將對象數據屬性中的 Enumerable 設置為了false
// 查看length的內部屬性
console.log(Object.getOwnPropertyDescriptor(this, 'length'));
// Object {value: 4, writable: false, enumerable: false, configurable: false}
(this)` 與this有所不同,不過遍歷結果卻是一樣,你可以在測試代碼中打印出來看看
專門用來遍歷DOMList
$('.list li').each(function (i, ele) {
console.log(i, ele);
// console.log(this==ele); // true
$(this).html(i);
if ($(this).attr('data-item')=='do') {
$(this).html('data-item: do');
};
})
因為domList并非數組,而是一個對象,只是因為其key值為0,1,2... 而感覺與數組類似,但是直接遍歷的結果如下
var domList=document.getElementsByClassName('its');
for(var item in domList) {
console.log(item, ':' + domList[item]);
}
// 0: <li></li>
// 1: <li></li>
// ...
// length: 5
// item: function item() {}
// namedItem: function namedItem() {}
因此我們在使用for in 遍歷domList時,需要將domList轉換為數組
var res=[].slice.call(domList);
for(var item in res) {}
類似這樣的對象還有函數的屬性 arguments 對象,當然字符串也是可以遍歷的,但是因為字符串其他屬性的 enumerable 被設置成了false,因此遍歷出來的結果跟數組是一樣的,也就不用擔心這個問題了.
for of 用于遍歷可迭代對象「Iterator」。在 JS 中,數組 Array,字符串 String, Map,Set 等,都是可迭代對象。
對象中包含 Symbol.iterator 屬性的,都被稱為可迭代對象。
var arr=[1, 2, 3];
arr[Symbol.iterator]
// ? values() { [native code] }
簡單案例。
const iterable=['react', 'vue', 'angular'];
for (const value of iterable) {
console.log(value);
}
如果你發現有些人寫函數這樣搞,不要驚慌,也不要覺得他高大上鳥不起
+function(ROOT, Struct, undefined) {
...
}(window, function() {
function Person() {}
})
()(), !function() {}() +function() {}() 三種函數自執行的方式
學習是一個艱苦的過程,當然如果能把技術學成,最后也一定可以獲得高薪工作。掌握一個好的學習方法,跟對一個學習的人非常重要。今后要是大家有啥問題,可以隨時來問我,能幫助別人學習解決問題,對于自己也是一個提升的過程。自己整理了一份2020最全面前端學習資料,從最基礎的HTML+CSS+JS到HTML5的項目實戰的學習資料都有整理web前端學習干貨,各種框架都有整理,送給每一位前端小伙伴,想要獲取的可以關注我的頭條號并在后臺私信我:前端,即可免費獲取
*請認真填寫需求信息,我們會在24小時內與您取得聯系。