JS循环一网打尽
while循环
特点:先判断后执行,只要条件为 true,循环能够一直执行代码块。
while循环()中的表达式,运算结果可以是各种类型,但是最终都会转为真假,转换规则如下:
- Boolean:true为真,false为假;
- String:空字符串为假,所有非空字符串为真;
- Number:0为假,一切非0数字为真;
- null/undefined/NaN:全为假;
- Object:全为真。
while (条件) {
要执行的代码块
}
function closest(el, selector) {
var matchesSelector = el.matches || el.webkitMatchesSelector
|| el.mozMatchesSelector || el.msMatchesSelector;
while (el) {
if (matchesSelector.call(el, selector)) {
break;
}
el = el.parentElement;
}
return el;
}
::: tip
使用场景:不知道我们需要执行多少次,不知道循环什么时候结束。
:::
do/while 循环
特点:先执行再判断,即使初始条件不成立,do-while循环至少执行一次,也就是说do-while循环比while循环多执行一次。
do {
要执行的代码块
} while (条件);
::: tip
使用场景:若希望至少执行一次语句块,可改用do…while语句。
:::
for in
特点:用于对数组或者对象的属性进行循环操作。
- 对于数组,迭代出来的是数组元素,对于对象,迭代出来的是对象的属性;
- 循环将遍历对象本身的所有可枚举属性,以及对象从其构造函数原型中继承的属性;
- 不能保证将以任何特定的顺序返回数组索引;
- 如果你只要考虑对象本身的属性,而不是它的原型,那么使用 getOwnPropertyNames() 或执行 hasOwnProperty() 来确定某属性是否是对象本身的属性。
for (variable in object) {...}
::: tip
使用场景:for … in是为遍历对象属性而构建的,不建议与数组一起使用,它最常用的地方应该是用于调试,可以更方便的去检查对象属性(通过输出到控制台或其他方式)。
跳出循环:break, continue。
:::
for of
特点:for … of循环是ES6引入的新的语法,用for … of循环遍历集合。
- for…of语句在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments, DOM NodeList, Generator对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句;
- for…of不能遍历普通对象(原因:普通对象没有Symbol.iterator属性,如果一个对象拥有Symbol.iterator属性,那么就可以使用for…of遍历);
for (variable of iterable) {...}
// 下面的例子摘自 阮一峰老师的ES6入门第三版
let obj = {
data: [ 'hello', 'world' ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
}
for (let value of obj) {
console.log(value)
} // hello world
// 类似数组的对象遍历 直接引用Array.prototype[Symbol.iterator]
// 普通对象部署数组的Symbol.iterator方法,并无效果
let iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
console.log(item); // 'a', 'b', 'c'
}
const person = {
name: 'John Smith',
job: 'agent'
};
for (const [prop, value] of Object.entries(person)) {
console.log(prop, value);
}
// 'name', 'John Smith'
// 'job', 'agent'
::: tip
使用场景:
跳出循环:break, continue, return。
:::
for await of
特点:只能在 async 函数或者 async 生成器里面使用。
- 迭代对象必须实现了iterator方法;
(async function(){
for await (const item of justjavac) {
console.log(item)
}
})();
::: tip
使用场景:多个异步请求串行请求。
:::
for
for (语句 1; 语句 2; 语句 3) {
要执行的代码块
}
1. 基础循环
for(var i=0; i<10; i++){
console.log(i);
}
// 输出0-9
2. 第三个条件缺省
for(var i=0; i<10;){
console.log(i++);
}
// 输出0-9
3. 无参数
for(;;){
console.log(1);
}
// 无限循环输出1
::: tip
使用场景:在脚本的运行次数已确定的情况下使用 for 循环。
跳出循环:break, continue。
思考:for循环中使用异步?
:::
forEach
特点:不会返回执行结果,而是undefined。
- 对于空数组则不会调用到匿名函数;
- 不可以链式调用。
array.forEach(callback(currentValue, index, array){
//do something
}, this)
//this:可选,执行会掉时候,this的指向((默认指向window)。
array.forEach(callback(currentValue, index, array){
//do something
})
// 数组改值
let arr = [1,3,5,7,9];
arr.forEach(item => {
item = 30;
})
console.log(arr);
//输出 (5) [1, 3, 5, 7, 9]
let arr = [1,3,5,7,9];
arr.forEach((item,index,arr) => {
arr[index] = 30;
})
console.log(arr);
//输出 (5) [30, 30, 30, 30, 30]
// 使用forEach可以跳过空元素
var arr = [1, 2, 3, 5, , 6, 7, 8, 9];
arr.forEach(function(item, index) {
console.log(item, index);
})
// 输出
1 0
2 1
3 2
5 3
6 5
7 6
8 7
9 8
// 使用forEach复制数组的方法 (全部复制,不跳过空元素)
var arr = [2, 4, 6, , 8, 3, 2];
var arr1 = [];
arr.forEach(function(item, index) {
arr1[index] = item;
})
console.log(arr1);
// 输出
[2, 4, 6, empty, 8, 3, 2]
::: tip
使用场景:forEach适合于你并不打算改变数据的时候,而只是想用数据做一些事情。
跳出循环:通过 throw err
:::
try {
var array = [1,2,3,4,5,6];
// 执行到第3次,结束循环
array.forEach((value)=>{
if(value > 3){
throw new Error("抛出异常跳出")
}
});
} catch(e) {
console.log(e)
};
map
特点:方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。
- 不会对空数组进行检测;
- 可以链式调用。
array.map(callback(currentValue, index, array){
//do something
}, this)
//this:可选,执行会掉时候,this的指向(默认指向window)。
array.map(callback(currentValue, index, array){
//do something
})
var arr = [3, 5, 7, 9, , 1, 2, 4];
var arr2 = arr.map((item, index, arr) => {
console.log(item);
});
// 输出
3 5 7 9 1 2 4
var arr = [3, 5, 7, 9, , 1, 2, 4];
var arr2 = arr.map((item, index, arr) => {
return "a";
});
console.log(arr2);
// 输出
["a", "a", "a", "a", empty, "a", "a", "a"]
var arr = [3, 5, 7, 9, , 1, 2, 4];
var arr3 = arr.map((item, index, arr) => {
return item + 10;
});
console.log(arr3);
// 输出
[13, 15, 17, 19, empty, 11, 12, 14]
var arr = [1, 2, 3, 4];
var arr4 = arr.map((item, index, arr) => {
item = item + 10;
});
console.log(arr4);
// 输出
[undefined, undefined, undefined, undefined]
::: tip
使用场景:遍历利出的数据需要处理并返回一个新数组,又不想改变原数组。
跳出循环:通过 throw err
:::
filter
特点:用于对数组进行过滤,它创建一个新数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
- 根据返回值是true还是false决定保留还是丢弃该元素;
- 不会对空数组进行检测;
- 不会改变原始数组。
array.filter(function(currentValue,index,arr), this)
// 可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值,
//如果省略了 thisValue ,"this" 的值为 "undefined"。
// 数组去重
var arr = [1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8];
var arr2 = arr.filter((item, index, self) => self.indexOf(item) === index)
console.log(arr2);
// 输出
[1, 2, 3, 4, 5, 6, 7, 8]
var arr3 = [1, 2, , 3, 4];
arr3.filter(item => {
console.log(item)
})
// 输出
1 2 3 4
::: tip
使用场景:筛选出一个对象列表中符合某个属性特征的列表。
:::
some
特点:用于检测数组中的元素是否满足指定条件。
- 依次执行数组的每个元素;
- 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测;
- 如果没有满足条件的元素,则返回false;
- 不会对空数组进行检测;
- 不会改变原始数组。
array.some(function(currentValue,index,arr), thisValue)
// 可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值,
//如果省略了 thisValue ,"this" 的值为 "undefined"。
var arr = [1, 2, 3, 4];
var ret = arr.some(item => {
console.log(item)
return item > 1
})
console.log(ret)
// 输出
1 2
true
every
特点:用于检测数组所有元素是否都符合指定条件。
- 依次执行数组的每个元素;
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false ,且剩余的元素不会再进行检测;
- 如果所有元素都满足条件,则返回 true;
- 不会对空数组进行检测;
- 不会改变原始数组。
array.every(function(currentValue,index,arr), thisValue)
// 可选。对象作为该执行回调时使用,传递给函数,用作 "this" 的值,
//如果省略了 thisValue ,"this" 的值为 "undefined"。
var arr = [2, 3, 1, 0];
var ret = arr.every(item => {
console.log(item)
return item > 1
})
console.log(ret)
// 输出
2 3 1
false
reduce
特点:方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
- 可以作为一个高阶函数,用于函数的 compose;
- 对于空数组是不会执行回调函数的。
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
// total 必需。初始值, 或者计算结束后的返回值。
// currentValue 必需。当前元素
// currentIndex 可选。当前元素的索引
// arr 可选。当前元素所属的数组对象。
// initialValue 可选。传递给函数的初始值
// 求数组项之和
var arr = [3, 9, 4, 3, 6, 0, 9];
var sum = arr.reduce((prev, cur) => {
return prev + cur;
}, 0);
console.log(sum)
// 输出
34
// 求数组项最大值
var max = arr.reduce((prev, cur) => {
return Math.max(prev, cur);
});
console.log(max)
// 输出
9
// 数组去重
var newArr = arr.reduce((prev, cur) => {
prev.indexOf(cur) === -1 && prev.push(cur);
return prev;
}, []);
console.log(newArr)
// pipe的实现
/*pipe 的实现也是reduce 的一个典型应用,pipe是一个 curry 化函数,
curry 化函数是一种由接受多个参数的函数转化为一次只接受一个参数的函数,
如果一个函数需要3个参数,那curry化后的函数会接受一个参数并返回一个函数来接受下一个函数,
这个函数返回的函数去传如第三个参数,最后一个函数会应用了所有参数的函数结果。*/
function pipe(...functions) {
return function(input) {
functions.reduce(
(preVal, fn) => fn(preVal),
input
)
}
}
reduceRight
特点:方法用法与reduce()其实是相同的,只是遍历的顺序相反,它是从数组的最后一项开始,向前遍历到第一项。
find
特点:返回通过测试(函数内判断)的数组的第一个元素的值。
- 当数组中的元素在测试条件时返回 true 时, 返回符合条件的元素,之后的值不会再调用执行函数;
- 如果没有符合条件的元素返回 undefined;
- 对于空数组,函数不会执行;
- 不该变数组的原始值。
array.find(function(currentValue, index, arr),thisValue)
// thisValue可选。 传递给函数的值一般用 "this" 值。
// 如果这个参数为空, "undefined" 会传递给 "this" 值
var arr = [1, 2, 3, 4, 5, 6];
var ret = arr.find(item => {
console.log(item);
return item > 2;
});
console.log(ret)
// 输出
1 2 3
3
findIndex
特点:传入一个测试条件(函数)符合条件的数组第一个元素位置。
- 当数组中的元素在测试条件时返回 true 时, 返回符合条件的元素的索引位置,之后的值不会再调用执行函数;
- 如果没有符合条件的元素返回 -1;
- 对于空数组,函数不会执行;
- 不该变数组的原始值。
array.findIndex(function(currentValue, index, arr),thisValue)
// thisValue可选。 传递给函数的值一般用 "this" 值。
// 如果这个参数为空, "undefined" 会传递给 "this" 值
性能大比拼
for ~= do while > for of > forEach ~= map ~= every > for in