「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」
简介
对象、数组的遍历在我们日常开发中基本上天天能碰到,但是对象、数组都有哪些遍历方法,各方法之间又有什么区别你们真的清楚了吗?今天笔者就来总结下。

对象的遍历
遍历对象的方法有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类型的属性,但不能获取原型链上的属性。
是Object.getOwnPropertyNames()和Object.getOwnPropertySymbols()的组合。
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);
// });
数组的遍历
遍历数组的方法有for、for in、for of、forEach、map,但是这些方法又都有各自的特点,我们来总结下
// 定义数组
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()方法,因为这些方法返回的是迭代器,所以只能通过forof来遍历
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 不能使用continue、break并且return的效果和前面的continue是一样的
arr1.forEach((item, index) => {
if (item == "c") {
// continue; // 不支持会报错
// break; // 不支持会报错
// return; // 跳过当次循环 类似前面的continue
}
console.log("forEach: ", item); // a b c d
});
map
map 一般我们很少使用map单独做循环,一般是利用map返回新数组的特性对原数组进行一些批量处理。
不能使用continue、break并且return的效果和前面的continue是一样的
arr1.map((item, index) => {
if (item == "c") {
// continue; // 不支持会报错
// break; // 不支持会报错
// return; // 跳过当次循环 类似前面的continue
}
console.log("map: ", item); // a b c d
});
总结
- 对象不能使用
for、for of、forEach、map方法进行遍历。 Object.entries()、Object.keys()、Object.values()不能获取Symbol属性、不能获取不可枚举属性、不能获取原型链属性。Object.getOwnPropertyNames()能获取不可枚举属性、不能获取Symbol属性、不能获取原型链属性。Object.getOwnPropertySymbols()只能获取Symbol属性。并且不管该Symbol属性是否是可枚举,都能遍历出来。for in获取的是键,不能获取Symbol属性、不能获取不可枚举属性、能获取原型链上的属性。Reflect.ownKeys()不但能获取自身不可枚举属性,还能获取Symbol类型的属性,但不能获取原型链上的属性。 是Object.getOwnPropertyNames()和Object.getOwnPropertySymbols()的组合。- 在遍历数组的时候
forEach、map只能使用return操作循环(效果和continue一样),不能使用continue、break。for forin forof能使用continue、break、return。 - 数组
keys() values() entries()方法,因为这些方法返回的是迭代器,所以只能通过forof来遍历。
扩展
使用 for of 遍历对象
前面我们说到,对象是不能使用for of来进行遍历的,那是怎么又可以呢?for of是ES6新增的,这个方法是基于迭代器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就是万能遍历方法,只要你实现了迭代器。
系列文章
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!