都2022年了你不会还没搞懂对象数组的遍历吧

简介: 对象、数组的遍历在我们日常开发中基本上天天能碰到,但是对象、数组都有哪些遍历方法,各方法之间又有什么区别你们真的清楚了吗?今天笔者就来总结下。

image.png

对象的遍历

遍历对象的方法有Object.keys()Object.values()Object.entries()Object.getOwnPropertyNames()Object.getOwnPropertySymbols()for in
Reflect.ownKeys(),但是这些方法又都有各自的特点,我们来总结下

// 定义对象
const obj1 = Object.create(
  { msg: "原型属性值" },
  {
    name: {
      value: "randy",
      writable: true,
      configurable: true,
      enumerable: true,
    },
    age: {
      value: 25,
      writable: true,
      configurable: true,
      enumerable: false,
    },
    [Symbol("test")]: {
      value: "symboltest",
      writable: true,
      configurable: true,
      enumerable: true,
    },
  }
);

Object.entries()Object.keys()Object.values()

Object.entries()Object.keys()Object.values()不能获取Symbol属性、不能获取不可枚举属性、不能获取原型链属性。

for (const entry of Object.entries(obj1)) {
  console.log("entries: ", entry); // ['name', 'randy']
}
for (const key of Object.keys(obj1)) {
  console.log("keys: ", key); // name
}
for (const value of Object.values(obj1)) {
  console.log("values: ", value); // randy
}

Object.getOwnPropertyNames()

Object.getOwnPropertyNames()能获取不可枚举属性、不能获取Symbol属性、不能获取原型链属性。

for (const name of Object.getOwnPropertyNames(obj1)) {
  console.log("getOwnPropertyNames: ", name); // name age
}

Object.getOwnPropertySymbols()

Object.getOwnPropertySymbols()只能获取Symbol属性。并且不管该Symbol属性是否是可枚举,都能遍历出来。

for (const symbol of Object.getOwnPropertySymbols(obj1)) {
  console.log("getOwnPropertySymbols: ", symbol); // Symbol(test)
}

for in

for in获取的是键,不能获取Symbol属性、不能获取不可枚举属性、能获取原型链上的属性。

function test() {
  for (const key in obj1) {
    if (key == "msg") {
      // continue; // 跳过当次
      // break; // 跳出循环
      // return; // 跳出循环
    }
    console.log("for in: ", key); // name msg
  }
}

test()

Reflect.ownKeys()

Reflect.ownKeys()不但能获取自身不可枚举属性,还能获取Symbol类型的属性,但不能获取原型链上的属性。

for (const key of Reflect.ownKeys(obj1)) {
  console.log("Reflect.ownKeys: ", key); // name age Symbol(test)
}

不能遍历对象的方法

对象不能用for循环进行遍历

// for (let i = 0; i < obj1.length; i++) {
//   console.log(obj1[i]);
// }

对象不能用for of循环进行遍历

// for (const property of obj1) {
//   console.log(property);
// }

对象不能用forEach循环进行遍历

// obj1.forEach((key) => {
//   console.log(key);
// });

对象不能用map循环进行遍历

// obj1.map((key) => {
//   console.log(key);
// });

数组的遍历

遍历数组的方法有forfor infor offorEachmap,但是这些方法又都有各自的特点,我们来总结下

// 定义数组
const arr1 = ["a", "b", "c", "d"];

for

for 循环获取的是下标

function arrFor() {
  for (let i = 0; i < arr1.length; i++) {
    if (i == 2) {
      // continue; // 跳过当次
      // break; // 跳出循环
      // return; // 跳出循环
    }
    console.log("arr for: ", arr1[i]); // a b c d
  }
}
arrFor();

for in

for in 循环获取的是下标,需要特别注意,获取的下标是string类型,并不是number类型

function arrForIn() {
  for (const index in arr1) {
    if (index == 2) {
      // continue; // 跳过当次
      // break; // 跳出循环
      // return; // 跳出循环
    }
    console.log("arr for in: ", index); // 0 1 2 3
  }
}

arrForIn();

for of

for of 循环获取的是值

function arrForOf() {
  for (const value of arr1) {
    if (value == "c") {
      // continue; // 跳过当次
      // break; // 跳出循环
      // return; // 跳出循环
    }
    console.log("arr for of: ", value); // a b c d
  }
}

arrForOf();

keys() values() entries()

跟对象类似,数组也有keys() values() entries()方法

for (const iterator of arr1.keys()) {
  console.log("keys: ", iterator); // 0 1 2 3
}

for (const iterator of arr1.values()) {
  console.log("values: ", iterator); // a b c d
}

for (const iterator of arr1.entries()) {
  console.log("entries: ", iterator); // [0, 'a'] [1, 'b'] [2, 'c'] [3, 'd']
}

forEach

forEach获取的是值

arr1.forEach((item) => {
  if (item == "c") {
    // continue; // 不支持会报错
    // break; // 不支持会报错
    // return; // 跳过当次循环 类似前面的continue
  }
  console.log("forEach: ", item); // a b c d
});

map

map 获取的是值,一般我们很少使用map单独做循环,一般是利用map返回新数组的特性对原数组进行一些批量处理。

arr1.map((item) => {
  if (item == "c") {
    // continue; // 不支持会报错
    // break; // 不支持会报错
    // return; // 跳过当次循环 类似前面的continue
  }
  console.log("map: ", item); // a b c d
});

总结

  1. 对象不能使用forfor offorEachmap方法进行遍历。
  2. forEachmap只能使用return操作循环(效果和continue一样),不能使用continuebreak

扩展

使用 for of 遍历对象

前面我们说到,对象是不能使用for of来进行遍历的,那是怎么又可以呢?for ofES6新增的,这个方法是基于迭代器Iterator来实现遍历的,意思就是只要你有了迭代器就能使用for of进行遍历,在js中,Array/Set/Map/String都默认支持迭代器,但是对象是没有实现该迭代器的,所以我们想要对象也能使用for of来进行遍历的话就需要实现该对象的迭代器了。

实现迭代器很简单,就是实现[Symbol.iterator]方法。

const obj = {
    [Symbol.iterator]:function(){}
}

[Symbol.iterator] 属性名是固定的写法,只要拥有了该属性的对象,就能够用迭代器的方式进行遍历。

迭代器的遍历方法是首先获得一个迭代器的指针,初始时该指针指向第一条数据之前

接着通过调用 next 方法,改变指针的指向,让其指向下一条数据

每一次的 next 都会返回一个对象,该对象有两个属性

  • value 代表想要获取的数据
  • done 布尔值,false表示当前指针指向的数据有值,true表示遍历已经结束

下面我们来看个例子

因为数组是默认实现了迭代器的,所以它肯定是有[Symbol.iterator]属性的,所以我们来看看

const arr = [1, 2, 3];
const it = arr[Symbol.iterator](); // 获取数组中的迭代器
// 执行迭代器的next()方法
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }

// for of其实输出的是next()对象里的value
for (const iterator of arr) {
  console.log(iterator); // 相继输出 1 2 3
}

所以接下来我们用对象作为例子,自己实现[Symbol.iterator]属性然后使用for of来进行遍历。

const user = { name: "randy", age: 24, sex: "male" };

user[Symbol.iterator] = function () {
  // 获取自身所有能遍历的key组成的数组
  const keys = Object.keys(user);
  let i = 0;
  return {
    next() {
      return {
        // 外部每次执行next都能得到数组中的第i个元素
        value: keys[i++],
        // 如果数组的数据已经遍历完则返回true
        done: i > keys.length,
      };
    },
  };
};

for (const iterator of user) {
  console.log(iterator); // 依次输出 name age sex
}

我们知道迭代器输出的是next方法返回对象的value,所以如果我们想遍历对象的时候返回的不是key而是value我们只需要稍微改下就可以了。

const user = { name: "randy", age: 24, sex: "male" };

user[Symbol.iterator] = function () {
  // 获取自身所有能遍历的value组成的数组
  const values = Object.values(user);
  let i = 0;
  return {
    next() {
      return {
        // 外部每次执行next都能得到数组中的第i个元素
        value: values[i++],
        // 如果数组的数据已经遍历完则返回true
        done: i > values.length,
      };
    },
  };
};

for (const iterator of user) {
  console.log(iterator); // 依次输出 randy 24 male
}

看到这是不是懂了for of的原理呢?其实for of就是万能遍历方法,只要你实现了迭代器。

系列文章

都2022年了你不会还没搞懂JS数据类型吧

都2022年了你不会还没搞懂JS原型和继承吧

都2022年了你不会还没搞懂JS赋值拷贝、浅拷贝、深拷贝吧

都2022年了你不会还没搞懂对象数组的遍历吧

都2022年了你不会还没搞懂this吧

都2022年了你不会还没搞懂JS Object API吧

都2022年了你不会还没搞懂js垃圾回收和内存泄露吧

都2022年你不会还没搞懂js执行上下文和事件循环机制吧

都2022年了你不会还没搞懂js中的事件吧

都2020年了你不会还没搞懂js异步编程吧

后记

感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!

相关文章
|
3月前
|
JavaScript 前端开发
数组嵌套数组去重
在JavaScript中对嵌套数组进行去重的方法,提供了一个具体的函数实现。
19 1
数组嵌套数组去重
|
3月前
|
C++
C++(十一)对象数组
本文介绍了C++中对象数组的使用方法及其注意事项。通过示例展示了如何定义和初始化对象数组,并解释了栈对象数组与堆对象数组在初始化时的区别。重点强调了构造器设计时应考虑无参构造器的重要性,以及在需要进一步初始化的情况下采用二段式初始化策略的应用场景。
|
Java
探索Java集合的3种遍历方式
传统的集合遍历方式 在Java中,我们可以使用传统的循环和迭代器来遍历集合
220 2
|
6月前
|
存储
Map集合,集合嵌套知识点
Map集合,集合嵌套知识点
26 0
|
7月前
|
存储 算法 Java
Java数组与集合的深入解析
Java数组与集合的深入解析
75 0
|
7月前
|
存储 Java 索引
【Java数组】数组定义 | 初始化赋值 | 数组练习题
【Java数组】数组定义 | 初始化赋值 | 数组练习题
|
前端开发
前端学习案例5-深拷贝的递归3数组判断
前端学习案例5-深拷贝的递归3数组判断
70 0
前端学习案例5-深拷贝的递归3数组判断
|
PHP 开发者
|
Go 开发者
二维数组的遍历方式|学习笔记
快速学习二维数组的遍历方式
二维数组的遍历方式|学习笔记
【Groovy】集合遍历 ( 操作符重载 | 集合中的 “ << “ 操作符重载 | 使用集合中的 “ << “ 操作符添加一个元素 | 使用集合中的 “ << “ 操作符添加一个集合 )
【Groovy】集合遍历 ( 操作符重载 | 集合中的 “ << “ 操作符重载 | 使用集合中的 “ << “ 操作符添加一个元素 | 使用集合中的 “ << “ 操作符添加一个集合 )
122 0
【Groovy】集合遍历 ( 操作符重载 | 集合中的 “ << “ 操作符重载 | 使用集合中的 “ << “ 操作符添加一个元素 | 使用集合中的 “ << “ 操作符添加一个集合 )
下一篇
DataWorks