JS 中的 for-in 和 of-of

for-in

for-in可遍历任何值,但常用于遍历对象。在遍历对象(起名A)时,我们可以获取到A自身的可枚举属性的key和A原型链上的可枚举属性的key,但如果A自身和原型链上的可枚举属性的key为Symbol时,无法被遍历到:

代码演示:

Object.prototype.extra = "haha";
const c = Symbol("c");
const obj = {
  a: "hi",  
  b: Symbol("c"), 
  [c]: 'ha' // 可不要把 [c] 写成 c了,前者表示变量c的值,后者表示字符c
};
for(let i in obj) {
  console.log(i);// a b extra 
}

// 遍历null不会报错,但也不会执行花括号内容,因为没啥可遍历的
for(let i in null) {
  console.log(i);
}

由于for-in会遍历到原型链上的内容,如果仅想遍历自身,则应使用hasOwnProperty(key)进行判断,该方法会判断key是否为调用者自身的属性:

Object.prototype.extra = "haha";
const obj = {a: "hi"};
for (let i in obj) {
  if (obj.hasOwnProperty(i))
    console.log(i);// a
}

for-of

for-of只能用于iterable objects的遍历(比如数组、字符串、类数组等),如果遍历非iterable对象是会报错的。在遍历中我们可以获取到被遍历者的value。

const arr = ["a", "b", "c"];
for(let i of arr) {
  console.log(i); // a b c
}

try {
  const obj = {};
  for(let i of obj) {}
} catch(e) {
  console.log(e) // TypeError: obj is not iterable
}

如果想要在遍历数组时获取到索引值,可以借助数组的entries()方法,该方法返回一个数组迭代器:

const arr = ["a", "b", "c"];
for(const [i, v] of arr.entries()) {
  console.log(`${i}-${v}`); // 0-a  1-b 2-c
}

除了内置提供的可迭代对象,我们还可以通过实现iterable protocol和iterator protocol来定义自己的iterable object,其中iterable protocol用来让对象可以被用于for-of,iterator protocol则是来定义遍历时的value具体如何变化:

const obj ={};
obj[Symbol.iterator] = function() {
  let val = 0;
  return {
    next:function() { // 必须要返回一个名为next的函数
      if (val === 3) 
        return { // next函数的返回值必须是一个含有done属性的对象,value属性则表明遍历时获取的key值
          done:true,
          value:val
        }
      return {
        done: false,
        value: val++
      }
    } 
  }
}
for(let val of obj) {
  console.log(val); // 0 1 2
}

for-await-of

for-await-of可用于遍历异步可迭代对象。这是ES2018中引入的,兼容性可能稍差一些。具体内容可点击蓝字查看,因为俺还没咋用过嘞。