for…infor…of有什么区别

for…in

1.遍历数组

const arr = ["element1", "element2", "element3"]

for (const item in arr) {
console.log("🚀 ~ for in", arr[item])
}
// 输出 element1 element2 element3

2.遍历对象

const obj = {
name: "obj",
age: 18,
}
for (const item in obj) {
console.log("🚀 ~ for in", item, obj[item])
}
// 输出:
// name obj
// age 18

检测是否可枚举

console.log("🚀 ~ ", Object.getOwnPropertyDescriptor(obj, "name"))
// 输出:
// { value: 'obj', writable: true, enumerable: true, configurable: true }
// enumerable: true 可枚举

3.遍历字符串

const str = "hello!world"
for (const item in str) {
console.log("🚀 ~ for in", str[item])
}
// 输出:
// h e l l o ! w o r l d

4.遍历字符串,包含unicode字符

const str2 = "a𠮷c"

for (const item in str2) {
console.log("🚀 ~ for in", str2[item])
}
// 输出:
// a � � c

5.遍历对象,父类属性也会遍历出来

const fatter = {
fatherAttr: 1
}

const instance = Object.create(fatter)

instance.a = 1;
instance.b = 2;
instance.c = 3;

for (const attr in instance) {
console.log(attr, instance[attr])
}

// 输出:
// a 1
// b 2
// c 3
// fatherAttr 1
// 将原型链上的属性也遍历出来了

// 通过 hasOwnProperty 判断是否是自身属性
for (const attr in instance) {
if (instance.hasOwnProperty(attr)) {
console.log(attr, instance[attr])
}
}

for…of

示例:

let str = 'a𠮷c';
for (const item of str) {
console.log(item)
}
// 输出:
// a 𠮷 c

遍历Map

const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
for (const mapElement of map) {
console.log(mapElement)
}
// 输出:
// [ 'a', 1 ] [ 'b', 2 ] [ 'c', 3 ]

// 可解构
for (const [key, value] of map) {
console.log(key, value)
}
// 输出:
// a 1 b 2 c 3

SetArray

  • entries()、keys()、values() 都返回一个迭代器对象
  • entries() 返回值为集合的键值对 [[key, value], [key, value], [key, value]](对于数组,key 就是索引值;对于 Set,key 就是 value)
  • keys() 返回值为集合的键名 [key, key, key](对于数组,key 就是索引值;对于 Set,key 就是 value)
  • values() 返回值为集合的值 [value, value, value]
const arr2 = ["a", "b", "c"];
let set = new Set(arr2);

// 1.entries
for (const entry of set.entries()) {
console.log(entry, "entries")
}
// 输出:
// [ 'a', 'a' ] [ 'b', 'b' ] [ 'c', 'c' ]

for (const arElement of arr2.entries()) {
console.log(arElement, "arElement")
}
// 输出:
// [ 0, 'a' ] [ 1, 'b' ] [ 2, 'c' ]

// 2.keys
for (const key of set.keys()) {
console.log(key, "keys")
}
// 输出:
// a b c
for (const key of arr2.keys()) {
console.log(key, "keys")
}
// 输出:
// 0 1 2

// 3.values
for (const value of set.values()) {
console.log(value, "values")
}
// 输出:
// a b c
for (const value of arr2.values()) {
console.log(value, "values")
}
// 输出:
// a b c

for…in 和 for…of 的区别

for…in for…of
key value 结果
可枚举属性 可迭代对象 遍历
遍历对象 遍历数组 常用

拓展操作

const father = {
fatherAttr: 1
}

const instance = Object.create(father)

instance.a = 1;
instance.b = 2;
instance.c = 3;

Object.defineProperty(instance, "d", {
value: 4,
enumerable: false, // 不可枚举
})

1.Object.Keys 获取键名数组

for (const key of Object.keys(instance)) {
console.log(key, "Object.keys")
}
// 输出:
// a b c

Object.keys 不会遍历原型链上的属性、以及自身不可枚举的属性

Object.keys 返回的是一个数组,所以可以使用数组的方法

  • ps: Object.keysfor in 的区别

    for (const instanceKey in instance) {
    console.log(instanceKey, "for in")
    }
    // 输出:
    // a b c fatherAttr

    for in 会遍历原型链上的属性,但是同样不会遍历自身不可枚举的属性

2.Object.getOwnPropertyNames() 获取键名数组

Object.getOwnPropertyNames() 与 Object.keys() 的区别在于,Object.getOwnPropertyNames() 会遍历自身不可枚举的属性

for (const instanceKey of Object.getOwnPropertyNames(instance)) {
console.log(instanceKey, "Object.getOwnPropertyNames")
}
// 输出:
// a b c d

3.Object.entries() 获取键值对数组

Object.entries() 不会遍历原型链上的属性和自身不可枚举的属性

for (const instanceKey of Object.entries(instance)) {
console.log(instanceKey, "Object.entries")
}
// 输出:
// [ 'a', 1 ] [ 'b', 2 ] [ 'c', 3 ]
  • ps: 当使用对象初始化一个Map实例,可以使用Object.entries()来初始化

    const obj = {a: 1, b: 2, c: 3};
    const map = new Map(Object.entries(obj));
    console.log(map.get("a")) // 1

4.Object.values() 获取对象的属性值数组

Object.values() 不会遍历原型链上的属性和自身不可枚举的属性

for (const instanceKey of Object.values(instance)) {
console.log(instanceKey, "Object.values")
}
// 输出:
// 1 2 3

5.Object.getOwnPropertySymbols()获取Symbol属性名

上面的方法都无法获取Symbol属性名,只能通过Object.getOwnPropertySymbols()来获取

instance[Symbol("e")] = 5;
for (const instanceKey of Object.getOwnPropertySymbols(instance)) {
console.log(instanceKey, "Object.getOwnPropertySymbols")
}
// 输出:
// Symbol(e)

什么是可枚举数据呢?

举个例子来说

const obj = { x: 100 }

我们使用 Object.getOwnPropertyDescriptors 方法获取指定对象所有的自有属性的属性描述符。

枚举

这时候发现他的每一个属性中 enumerable 都是 true,这时候他就是可枚举的。

如果一个属性的enumerable为false,下面三个操作不会取到该属性。

for..in循环

Object.keys方法

JSON.stringify方法

所以一般情况下 对象、数组、字符串都是可枚举的

什么是可迭代数据呢?

for…of用于可迭代的数据,如:

  • 数组
  • 字符串
  • Map
  • Set
const arr = [1, 2, 3]

数组中有个 Symbol.iterator的属性:

  • 1.只要一个数据已经实现了Iterator接口, 那么这个数据就有一个叫做[Symbol.iterator]的属性
  • 2.[Symbol.iterator]的属性会返回一个函数
  • 3.[Symbol.iterator]返回的函数执行之后会返回一个对象
  • 4.[Symbol.iterator]函数返回的对象中又一个名称叫做next的方法
  • 5.next方法每次执行都会返回一个对象{value: 1, done: false}
  • 6.这个对象中存储了当前取出的数据和是否取完了的标记

迭代

就类似设计模式中的迭代器模式:

不断有next、next去调用:

  • A next() => B
  • B next() => C
  • 如此 如此

了解了上面的情况之后,我们也可以理解什么时候用 for…of 什么时候用 for…in?

  • for…in可以用在可枚举的数据
  • for…of用于可迭代的数据