JavaScript中for...in 和 for...of 的区别

for…in 和 for…of 的区别是什么

先贴一个mdn上对于 for…in 和 for…of 的解释:
在这里插入图片描述

for…in

for…in 比较容易理解,用于迭代对象的可枚举字符串属性(包括原型链上的可枚举字符串属性),直接上 Demo:

const object1 = {
  1: 44, 
  [Symbol.for('property4')]: 45
};

object1.__proto__.say = () => {}

Object.defineProperties(object1, {
  property1: {
    value: 42,
    writable: true,
  },
  property2: {
    value: 43,
    enumerable: true,
  },
});

for (let name in object1) {
  console.log(name)
}
// 1 [object String]
// property2 [object String]
// say [object String]

打印结果只有 ‘1’ 和 ‘property2’ 和 'say’这三个属性,逐个分析:

  • 1 作为对象的属性时默认会被转为 string 类型,且通过对象字面量方式定义的属性,默认为可枚举属性(enumerable 为 true);
  • Symbol.for(‘property4’) 未打印,是因为 Symbol 类型不是字符串类型,因此 for…in 无法迭代;Symbol 类型的属性不会出现在for…in、for…of 循环中,也不会被 Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。需要通过Object.getOwnPropertySymbols()方法,可以获取指定对象的所有 Symbol 属性名。
  • property1 属性属于字符串类型,但在数据描述中未显示定义enumerable,默认为 false,为不可枚举属性,因此 for…in 无法迭代;(如果你想要所有以字符串为键(不包括 Symbol)的自有属性(不包括原型链),包括不可枚举的属性,可以使用 Object.getOwnPropertyNames()。)
  • property2 属性属于字符串类型,在数据描述中显示定义enumerable为 true,为可枚举属性,因此 for…in 可以迭代;
  • say 属性在 object1 的原型上,显示定义,可枚举;

for…of

接下来我们来看下 for…of 的解释:for…of 语句用于迭代可迭代对象定义的要进行迭代的值
老规矩,我们先上个小 Demo:

// 继续延用 objetc1 对象
for (let value of object1) {
  console.log(value, Object.prototype.toString.call(name))
}
// TypeError: object1 is not iterable

好家伙:直接报错了,报错信息告诉我们 object1 是不可迭代的,即 object1 并不是 可迭代对象

让我们再仔细回看下 mdn 对于 for…of 的解释:for…of 语句用于迭代可迭代对象定义的要进行迭代的值

这里有两个重点:一是可迭代对象,二是定义的要进行迭代的值

那么问题来了,什么是可迭代对象 呢❓

顺其而然,迭代协议 就应运而生了,符合 迭代协议 的对象就是 可迭代对象 啦。

迭代协议 具体分为两个协议:可迭代协议迭代器协议可迭代对象 必须同时满足这两个协议,才能成为 可迭代对象

在这可以先跟大家通俗的简单介绍一下这两个协议:

  • 可迭代协议】可以简单理解为是否有 @@iterator 方法;
  • 迭代器协议】可以简单理解为 @@iterator 方法返回的对象是否符合IteratorResult 接口类型定义(interface IteratorResult { value: any; done: boolean; })

想更多了解这两个协议的可以具体往下看:

可迭代协议允许 JavaScript 对象定义或定制它们的迭代行为,例如,在一个 for…of 结构中,哪些值可以被遍历到。一些内置类型同时是内置的可迭代对象,并且有默认的迭代行为,比如 Array 或者 Map,而其他内置类型则不是(比如 Object)。

要成为可迭代对象,该对象必须实现 @@iterator 方法,这意味着对象(或者它原型链上的某个对象)必须有一个键为 @@iterator 的属性,可通过常量 Symbol.iterator 访问该属性; @@iterator 方法是一个无参数的函数,其返回值为一个符合【迭代器协议】的对象。

当一个对象需要被迭代的时候(比如被置入一个 for…of 循环时),首先,会不带参数调用它的 @@iterator 方法,然后使用此方法返回的迭代器获得要迭代的值。
回看上面的例子,为什么在 for…of 迭代 object1 的时候会报错,因为 object1 是不可迭代对象,为啥是不可迭代对象,因为该对象未实现 @@iterator 方法。

那我们该如何让 object1 成为可迭代对象呢❓我们需要在 object1对象上 实现@@iterator 方法。

使用生成器函数:
const myIterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};
Object.assign(object1, myIterable)
for (let value of object1) {
  console.log(value, Object.prototype.toString.call(name))
}
// 1 '[object String]'
// 2 '[object String]'
// 3 '[object String]'
使用自定义迭代器对象(next()返回对象需要符合迭代器协议):
const myIterable = {
  data: [1, 2, 3],
  pointer: 0,
  [Symbol.iterator]() {
    return {
      next: () => {
        if (this.pointer < this.data.length) {
          return { value: this.data[this.pointer++], done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};
Object.assign(object1, myIterable)
for (let value of object1) {
  console.log(value, Object.prototype.toString.call(name))
}
// 1 '[object String]'
// 2 '[object String]'
// 3 '[object String]'

题外扩展:
Array.prototype.values() 方法返回一个新的数组迭代器对象,可以使用该迭代器对象迭代数组中每个元素的值,使用迭代器对象可以调用 next() 方法。
Array 内部实现了 @@iterator,因此可以直接放进 for…of 中进行迭代。
Array.prototype.values() 是 Array.prototype@@iterator 的默认实现。

Array.prototype.values === Array.prototype[Symbol.iterator]; // true
const arr = ["a", "b", "c", "d", "e"];

for (const value of arr) {
  console.log(value);
} // "a" "b" "c" "d" "e"

const iterator = arr.values();

for (const letter of iterator) {
  console.log(letter);
} // "a" "b" "c" "d" "e"

iterator.next(); // { value: undefined, done: true } 因为iterator迭代器每次都是一次性的,想要重新迭代使用必须再重新调用一个values()返回一个新的迭代器对象

for (const letter of iterator) {
  console.log(letter);
} // 不再打印
  • 38
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值