azl397985856 / fe-interview

宇宙最强的前端面试指南 (https://lucifer.ren/fe-interview)
Apache License 2.0
2.83k stars 260 forks source link

【每日一题】- 2020-07-11 - iterable 知多少? #136

Closed azl397985856 closed 3 years ago

azl397985856 commented 4 years ago

如下代码会报错: Uncaught TypeError: object is not iterable

const obj = {
  key1: 'value1',
  key2: 'value2',
  key3: 'value3',
};
for(const v of obj) {
  console.log(v);
}

而这样却不会:

const obj = {
  key1: 'value1',
  key2: 'value2',
  key3: 'value3',
};
for(const k in obj) {
  console.log(k);
}
  1. 为什么第一个报错,第二个却不会?
  2. 如何改造使得 for...of 遍历对象却可以不报错?
azl397985856 commented 4 years ago

为什么第一个报错,第二个却不会?

for...of

使用 for...of 遍历"对象"需要该对象显式实现Symbol.iterator 方法,具体参考 MDN。由于 Object.prototype 上没有 Symbol.iterator

Object.prototype.hasOwnProperty(Symbol.iterator) // false

因此通过 for...of 迭代对象会报错。

而数组的原型上是有的,因此for...of 数组是不会有问题的。

Array.prototype.hasOwnProperty(Symbol.iterator) // true

for...in

for...in 语句以任意顺序迭代对象的可枚举属性。和 for...of 的区别在于:

eg:

for (let a in 'aaaaa') {
    console.log(a) // 依次输出 0,1,2,3,4
}

Object.defineProperty(String.prototype, 'toString', { enumerable: true})

for (let a in 'aaaaa') {
    console.log(a) // 依次输出 0,1,2,3,4,toString
}

更多参考: ECMA

如何改造使得 for...of 遍历对象却可以不报错?

上面我已经分析了原因。那么实现也很简单。只需要手动实现 Object.prototye[Symbol.iterator] 即可。

代码:

    Object.prototype[Symbol.iterator] = function() {
        let index = 0;
        const properties = Object.keys(this);
        let done = false;
        return {
            next: () => {
                done = index >= properties.length;
                const obj = {
                    done,
                    value: this[properties[index]],
                    key: properties[index]
                };
                index++;
                return obj;
            }
        };
    }
suukii commented 4 years ago

Short Answer

const obj = {
    key1: 'value1',
    key2: 'value2',
    key3: 'value3',
    [Symbol.iterator]: function* () {
        const values = Object.values(this)
        let i = 0
        while (i < values.length) {
            yield values[i]
            i++
        }
    }
}

Long Answer

maybe I'll come back. oh ~ maybe

stale[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.