Javascript 中 for…in 与 for…of 的区别

TLDR:

  • for…in 以任意顺序迭代对象的可枚举属性(包括原型链上的属性);
  • for…of 遍历迭代对象定义要迭代的数据;

Object 类型

const obj = {
    name: 'wayne'
}
obj.age = 12
Object.defineProperty(obj, 'sex', { value: 'male' })

for (const item in obj) {
    console.log(item)   // 'name', 'age'
}

for (const item of obj) {   // TypeError: obj is not iterable
    console.log(item)
}

for…in 迭代对象的可枚举属性,所以输出:name、age。
没有输出 sex ,是因为通过 Object.defineProperty() 定义的属性,其属性描述符 enumerable 默认为 false。
直接通过赋值和属性初始化的属性,属性描述符 enumerable 默认为 true。

for…of 遍历迭代对象定义的数据。
为了实现可迭代,一个对象必须实现 @@iterator 方法,即会有一个 Symbol.iterator 属性。
由于 obj 没有实现可迭代协议,所以 for…of 一个 Object 则会报错。

Symbol.iterator in {}
// false

Symbol.iterator in []
// true

Array 类型

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};

const arr = [1, 2]
arr.foo = 'hello';

for (const item in arr) {
    console.log(item)    // '0', '1', 'foo', 'arrCustom', 'objCustom'
}

for (const item of arr) {
    console.log(item)    // 1, 2
}

for…in 遍历 Array 类型时依然遵循遍历所有可枚举属性原则。
数组中每一个元素都是可枚举的,并且其可枚举属性就是下标(与 Javascript 数组的底层实现方式契合)。
并且数组自身原型链上的可枚举属性也都会被 for…in 列出,通常 for…in 会结合 hasOwnProperty 一起使用。
但是即使使用 hasOwnProperty 也只能过滤继承而来的可枚举属性,而自身的可枚举属性依然会被 for…in 遍历。

在 for…of 中,由于 Array 类型是内置可迭代对象,所以会按照 @@iterator 定义的规则来进行迭代。
所以 “for…of 用来迭代对象的 value” 这样描述并不准确,仅仅是因为在内置默认的迭代器中返回了对象的 value,对于迭代器的默认行为我们完全可以自定义其规则。

其他数据类型

String

const str = 'hello'

for (const item in str) {
    console.log(item)    // '0', '1', '2', '3', '4',
}

for (const item of str) {
    console.log(item)    // 'h','e','l','l','o',
}

Set

const set = new Set([1,2,3,4,5])

for (const item in set) {
    console.log(item)    // 无输出
}

for (const item of set) {
    console.log(item)    // 1, 2, 3, 4, 5
}

内置可迭代对象

从数据类型的支持程度来看 for…of 要优于 for…in。以下的内置可迭代对象均可用于 for…of:StringArrayTypedArrayMap 、Set 以及 ArrayBufferNodeList 等 类数组和 arguments 对象。

发表评论

邮箱地址不会被公开。 必填项已用*标注