对象的遍历
遍历对象的方法有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);
// });
数组的遍历
遍历数组的方法有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()
方法
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
});
总结
- 对象不能使用
for
、for of
、forEach
、map
方法进行遍历。 forEach
、map
只能使用return
操作循环(效果和continue
一样),不能使用continue
、break
。
扩展
使用 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
就是万能遍历方法,只要你实现了迭代器。
系列文章
后记
感谢小伙伴们的耐心观看,本文为笔者个人学习笔记,如有谬误,还请告知,万分感谢!如果本文对你有所帮助,还请点个关注点个赞~,您的支持是笔者不断更新的动力!