前端JavaScript对数组的所有循环操作方法以及使用区别

文章目录

  • 前言
  • 一、for()(数组)
  • 二、for in()(对象/数组)
  • 三、for of(对象/数组)
  • 四、forEach()(数组)
  • 五、find()(数组)
  • 六、findIndex()(数组)
  • 七、every()(数组)
  • 八、some()(数组)
  • 九、reduce()(数组)
  • 十、map(数组)
  • 十一、flat()
  • 十一、flatMap()
  • 十二、filter()
  • 二、Object.keys、values、entries()
  • 十二、concat()
  • 总结


前言

知其然,知其所以然
真的很重要,带着一知半解去,那么构建起来的知识就如同空中楼阁一般,禁不起推敲和实践。记得曾今学过JavaScript的很多数组的方法,但到头来只是写出一个一个的 for 循环,每种类型间的区别都不清楚,那么怎么能明白灵活使用呢?这里我们重新去学习,了解每一个的特点,以及他所对应的使用场景。做到心中有数。才能对拿到的数据做出适合我们的调整和组合。

哈哈哈,其实也没啥,就学习和运用一下JavaScript的关于数组的api,以及便于自己日常忘了的时候来查阅,并且方便自己添加使用过程中遇到的问题和技巧。


一、for()(数组)

  • 没啥好说的啦,老朋友了,但实际上,特别当你刚入职,进入到公司中时,你会看到公司的代码中,实际上很少用到,往往会用其它一些来完成一些特定的目的。并且写出的代码非常简洁漂亮。
  • 可否提前中断:可提前中断
for(var i = 0; i < len.length; i++){  
    console.log("你好帅呀!");  
} 
//这是展示一种优化后的
for(var i = 0,len = XXX.length; i < len; i++){  
    console.log("你好帅呀!");
} 

为什么这么写就能优化循环呢?

其实我们每次循环的过程会有几个步骤(以上面的代码为例):

  • 1.获取控制条件的值,在这里是指 len.length 的值,我们保存为局部变量,就不用每次的获取了(当然,要是这里为 i < 5 是一个确切的数,那就是最优的了,而我们把length写成局部变量就是为了此)
  • 获取到控制条件的值后,我们就会进行判断,符合就下一步,不符合就结束(i < len.length)
  • 要是上一步的条件符合,那么就到自增(i++)
  • 接着执行代码循环体内容(这里是输出:你好帅呀!)

所以我们可以看出,当我们把获取长度的,变为局部变量,就能减少执行,达到优化的目的了。

虽然可怜的for循环已近渐渐离开我们的身边了,泪目,想当年,实习前,它还陪伴我度过无数个日夜。

二、for in()(对象/数组)

  • 官方解释:for…in 语句用于遍历数组或者对象的属性(对数组或者对象的属性进行循环操作)
  • 可否提前中断:可提前中断
  let person = {
    name: {
      firstName: 'lala',
      secondName: 'wawa'
    },
    age: 16,
    sex: 'boy'
  }
  person.hobby = 'song'
  for (item in person) {
    console.log(person[item])
  }
  console.log('*************')
  for (item in person) {
    console.log(item)
  }
//输出结果
//{firstName: "lala", secondName: "wawa"}[[Prototype]]
 //16
 //boy
 //song
 //*************
 //name
 //age
 //sex
 //hobby

//输出数组

let arr = [1,2,3];
for(let item in arr){
 console.log( '------' , item, arr[item])
 console.log('****' ,item+item);
}

//输出结果

//------ 0 1
//**** 00
//------ 1 2
//**** 11
//------ 2 3
//**** 22

那么我们一般什么时候用到这个循环方法呢?一般在我们需要遍历对象,获取属性的时候用到多点,当然,遍历数组也是可以的。但是不建议,因为我们可以看出上面,当操作数组的时候,返回的item是字符串格式,不是数组下标的形式所以,返回了00,11,22,而不是0 ,2,4。这个是个需要注意的地方。
特点

  • 可以看出首先对于对象返回的是键值名,对于数组返回的是数组下标的字符串形式
  • 不仅返回自身的,还会沿着原型链找,遍历上面的(这也是导致他效率低的原因,可以使用 **obj[i].hasOwnProperty(i)**来避免)

他的效率比较低的具体分析,后面再来详细研究一下为啥吧!

三、for of(对象/数组)

  • 官方解释:在可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments 对象等等)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句
  • 可否提前中断:可提前中断
let iterable = new Map([["a", 1], ["b", 2], ["c", 3]]);

for (let entry of iterable) {
  console.log(entry);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]

for (let [key, value] of iterable) {
  console.log(value);
}
// 1
// 2
// 3

我们就看一个map的例子就好了,因为它的使用和 for in大体是相同的,来自ES6的语法,他解决了很多for in会带来的困惑,使得更加的好用,但我们要留意兼容性。下面我们来看看他们的区别,以及改进了什么。

for of和for in的区别

四、forEach()(数组)

  • 官方解释:forEach() 方法对数组的每个元素执行一次给定的函数
  • 可否提取中断:不可直接中断(回调函数另说)
  • 是否改变原数组:不改变
//回调函数可以有三个值(当前值,当前值索引,数组对象本身)
let arr = [1,2,3,4,5,6];
arr.forEach((item,index ,arr)=>{
    console.log("------>item" , item);
    console.log("------>index" , index);
    arr[index] = item +1;
    console.log("------>addarr" , arr);
})
//输出结果
------>item 1
------>index 0
------>addarr [ 2, 2, 3 ]
------>item 2
------>index 1
------>addarr [ 2, 3, 3 ]
------>item 3
------>index 2
------>addarr [ 2, 3, 4 ]

根据它的特点,那么我们常用的情况肯定是对于数组的操作,并且这个过程不需要中断,且对原数组不会造成影响,如果有其他的要求,那么建议使用另外一种方法来操作比较适合了,另外他不支持 continue,非要的话,用 return false 或 return true 代替。break,可以用 try catch/every/some 代替。

forEach中return无效

为什么呢?

因为:forEach()无法在所有元素都传递给调用的函数之前终止遍历,也就是不可中断

forEach 和 for 关于return 的区别

直接在 for 循环中使用 return会报错,在 forEach 中使用 return 不会报错,但是并不会生效,如果我们真的需要 return 返回某个值,只能把 return 操作放在函数中,而在 forEach() 中的 return 只会结束此次循环,执行下一次循环,但 for 中的 return 会直接跳出循环,不再执行下面的循环

五、find()(数组)

  • 官方解释:find() 方法返回数组中满足提供的测试函数的第一个元素的值。没有符合的就会返回 undefined。对于空数组不会执行。
  • 可否提取中断:可中断
  • 是否改变原数组:不改变
//同上面的forEach,回调函数有三个值(当前值,当前值索引,数组对象本身)
let arr = [1,2,3,4,5,6];
const find = arr.find(item=>item>3)
console.log(find);
//输出结果
4
//还可以下面这种用法,用一个方法的形式。麻烦的操作,非要实现的话,可以用很多其他的方法,没必要这样~~~~
var inventory = [
    {name: 'apples', quantity: 2},
    {name: 'bananas', quantity: 0},
    {name: 'cherries', quantity: 5}
];

function findCherries(fruit) {
    return fruit.name === 'cherries';
}

console.log(inventory.find(findCherries)); 
//输出结果 
//{ name: 'cherries', quantity: 5 }

这玩意是ES6的,要注意兼容性。

六、findIndex()(数组)

  • 官方解释:findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1,对于空数组不执行。
  • 可否提取中断:
  • 是否改变原数组:不改变
let arr = [1,2,3,4,5,6];
const findIndex = arr.findIndex(item=>item>3)
console.log(findIndex);
//输出结果
//3

俺就不明白为啥还有这个方法,这个作用不是可以在find里面就能实现吗?迷惑ing

七、every()(数组)

  • 官方解释:every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值,如果所有元素都通过检测返回 true,否则返回 false。为空的时候,返回 true。
  • 可否提取中断:
  • 是否改变原数组:不改变
//回调函数有三个值(当前值,当前值索引,数组对象本身)
const iftrue = (item) => item < 5;
const arr = [1, 34];
console.log(arr.every(iftrue));
// 输出结果
//true

一旦找到一个不符合函数条件的,就会终止迭代,返回false

八、some()(数组)

  • 官方解释:some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。为空时,返回false。
  • 可否提取中断:
  • 是否改变原数组:不改变
//回调函数有三个值(当前值,当前值索引,数组对象本身)
const iftrue = (item,index,arr) => item < 5;
const arr = [10, 84];
console.log(arr.some(iftrue));
// 输出结果
//true

只要有一个符合就会返回 true, 反之返回 false。他和 every有点相似的,区别在于,前者是全部通过才为 true ,后者是只要有一项符合就为true

九、reduce()(数组)

  • 官方解释:reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。数组为空的时候不会执行。
  • 可否提取中断:
  • 是否改变原数组:不
//回调函数可以传入有四个值reduce(accumulator, currentValue, index, array )前面两项是必须传入的。
const arr = [1, 3, 4, 5];
console.log(arr.reduce( (accum ,item) => accum + item));
//输出结果
//8

该方法在运行的过程中,我们知道有四个参数,前面两个是必须传的。

  • 第一个参数,也就是 accum 起始为 1 ,回调函数会指向当前索引(index = 1),获取到当前索引的值(item = 3),返回的回调函数方法题内执行后的结果,这时是第一次调用,返回(return = 4)。
  • 第二次调用的时候,第一个参数的值就会变为上一次返回的值(accum = retuen=4),同时当前索引指向下一位,获取到下一位索引的值(item=2),返回的回调函数方法题内执行后的结果,这时是第二次调用,返回(return = 8)。
  • 第三次调用的时候,第一个参数的值就会变为上一次返回的值(accum = retuen=8),同时当前索引指向下一位,获取到下一位索引的值(item=3),返回的回调函数方法题内执行后的结果,这时是第三次调用,返回(return = 13)。

在这里你是否发现,我们本身数租是长度为 4 的,但是方法只是执行了三遍就结束了。并且在方法第一次循环的开始,并不是从索引为 0 开始,是从 1 开始。

下面我们就说看看该方法除了回调函数外的另一个参数 initialValue,这个参数是作为 accun的初始值,没提供的话,会默认为数组的第一项。那么我们上面的例子,要是我们给 accum 设置初始值为 0,那么这个执行的过程就会多了一次,变为四次,索引从0开始。并且使用索引值还可以避免数组为空的时候导致的报错现象。

下面看看,一些MDN提供的一些好的用法,来带个大家在使用上的一些启示吧!更多的大家可以去官网看看!

数组去重

let myArray = ['a', 'b', 'a', 'b', 'c', 'e', 'e', 'c', 'd', 'd', 'd', 'd']
let myOrderedArray = myArray.reduce(function (accumulator, currentValue) {
  if (accumulator.indexOf(currentValue) === -1) {
    accumulator.push(currentValue)
  }
  return accumulator
}, [])

console.log(myOrderedArray)

将二维数组转化为一维

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
  function(a, b) {
    return a.concat(b);
  },
  []
);
// flattened is [0, 1, 2, 3, 4, 5]

十、map(数组)

  • 官方解释:map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
  • 可否提取中断:
  • 是否改变原数组:不改变
//回调函数有三个值(当前值,当前值索引,数组对象本身)
let numbers = [1, 4, 9];
let roots = numbers.map(Math.sqrt);
let quent = numbers.map(item => [item * 2])
console.log("---------->number" , number)
console.log("---------->roots" , roots)
console.log("---------->quent" , quent)
//输出结果为: 
//---------->number[1, 4, 9]
//---------->roots[1, 2, 3]
//---------->quent[[1], [4], [9]]

根据他返回一个新的数组的特点,当我们需要保留原数组不变,并且生成新的数组时,我们可以用该方法。

十一、flat()

  • 官方解释:flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
  • 可否提取中断:
//默认传入的参数为1,可以传入不同的数值来指定扁平的深度
const arr = [0, 1, 2, [3, 4, [5] ] ];
console.log('--------->' , arr.flat());
console.log('********>' , arr.flat(2));
//输出结果:
// --------->[0, 1, 2, 3, 4, [5] ]
//'********>[0,1,2,3,4,5 ]

除了扁平化数组外,还可以去除数组中的空项的。另外,要扁平化数组的话,还可以用展开运算符(…)

十一、flatMap()

  • 官方解释:flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 连着深度值为1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
  • 可否提取中断:
//回调函数有三个值(当前值,当前值索引,数组对象本身)
let arr1 = [1, 2, 3, 4];

console.log('----->1' , arr1.map(x => [x * 2]));
console.log('----->2' , arr1.flatMap(x => [x * 2]));
console.log('----->3' , arr1.flatMap(x => [[x * 2]]));
console.log('----->4' , arr1.map(x => [x * 2]).flat(1));
//输出结果
//----->1: [ [ 2 ], [ 4 ], [ 6 ], [ 8 ] ]
//----->2: [ 2, 4, 6, 8 ]
//----->3: [ [ 2 ], [ 4 ], [ 6 ], [ 8 ] ]
//----->4: [ 2, 4, 6, 8 ]

可以看出flatMap其实也就是前面两者,flat 和 map的结合,我们用flat和map也能实现flatmap的效果

十二、filter()

  • 官方解释:filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。不会对空数组处理,会直接返回空数组。
  • 可否提取中断:
  • 是否改变原数组:不改变
//回调函数有三个值(当前值,当前值索引,数组对象本身)
const arr = [10, 84 , 6 , 9 , 3];
console.log(arr.filter(item => item > 5));
// 输出结果
//[10, 8, 6, 9 ]

该方法中,符合回调函数的执行就会返回标位 true ,最后会把所有标位 true的值返回为一个新数组,我们可以通过回调函数来筛选我们需要的值。

二、Object.keys、values、entries()

Object.keys(obj) —— 返回一个包含该对象所有的键的数组。
Object.values(obj) —— 返回一个包含该对象所有的值的数组。
Object.entries(obj) —— 返回一个包含该对象所有 [key, value] 键值对的数组。

const obj = {
    a: 25,
    b: 42,
    c: 62,
    d: 82,
  };
  for (const [key, value] of Object.entries(obj)) {
    console.log(value*2);
  }
  for (const item of Object.keys(obj)) {
    console.log('--------->key',item);
  }
  for (const item of Object.values(obj)) {
    console.log('--------->values',item);
  }
  console.log('--------->',Object.entries(obj).map(([key, value]) => [key, value * 2]));
  //输出结果
50
84
124
164
--------->key a
--------->key b
--------->key c
--------->key d
--------->values 25
--------->values 42
--------->values 62
--------->values 82
---------> [ [ 'a', 50 ], [ 'b', 84 ], [ 'c', 124 ], [ 'd', 164 ] ]

十二、concat()

  • 官方解释:concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
  • 可否提取中断:
  • 是否改变原数组:不会
var alpha = ['a', 'b', 'c'];
var numeric = [1, 2, 3];

alpha.concat(numeric);
// result in ['a', 'b', 'c', 1, 2, 3]

总结

迭代的方法非常的多,我们如何选择最合适的呢?一般我们先看我们需要操作的对象是数组还是对象,另外根据我们的需要,是否改变原函数,其次迭代在进行的过程中,是否需要中断。通过这几个条件,我们就能选择出适合我们需求的方法了。

还没写完~~~~还很多细节没补充,以及打算做成一张图的形式更加的直观,先发吧,写了几天腻了~

你可能感兴趣的:(其他,前端,数组)