字节面试官问:你能不能实现把对象解构到数组上?

3,379 阅读5分钟

前言

本文主要介绍 ES6 的 Iterator, Iterator 可以说在ES6中非常重要!是面试官经常会被问到的问题!坚持看到最后哟,后面才是重头戏!

一、for...of

在进入正题之前,我们先来了解下for...of循环的一些特性,它是ES6的标准,作为遍历所有数据结构统一的方法。

1.遍历数组返回的是元素

const arr=[1,2,3,4]
for(let item of arr){
  console.log(item); //1 2 3 4
}

可以看到打印输出数组每一个元素。

2.不能遍历不具有 iterator 属性的数据结构

let obj={
  a:1,
  b:2,
  c:3
}
for(let item of obj){
  console.log(item); //TypeError: obj is not iterable
}

可以看到遍历输出对象的元素时会报错: obj is not iterable,也就是obj不是可迭代的

3.不能遍历到数组原型上的属性

Array.prototype.c='d'//往数组原型上挂一个属性c
const arr=[1,2,3,4]
for(let item of arr){
  console.log(item);  //1 2 3 4
}

可以看到打印输出的依旧是1 2 3 4,没有输出数组原型上的属性。

由以上我们可以知道for...of遍历具有iterator 属性的数据结构,而数组就有iterator 属性,所以可以用 for...of遍历,而对象身上没有iterator 属性所以不可以用for...of遍历(一些特例除外)。

你们可能会想iterator到底是什么东西,for...of和我们接下来要讲的iterator迭代器有什么关联吗? 请看下文!

二、iterator(迭代器)

Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即for...of循环。Iterator 接口主要供for...of消费。

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。

可能会有点晕?来个例子!

image.png

我们在浏览器的控制台里输入一个数组并输出,可以看到数组里的[[Prototype]]内包含许多属性,其中就有Symbol.iterator属性。

同时我们可以注意到:Symbol.iterator属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。

看下例:

let arr=[]

// arr={
//   [Symbol.iterator]:function(){
//     return //迭代器对象
//   }
// } arr数组上有这样一个属性Symbol.iterator 外层加了[]代表此属性只可访问不可更改

image.png

可以看到数组身上的Symbol.iterator属性返回的一个函数,既然是个函数就能调用:

image.png

返回的是一个迭代器对象Array Iterator{}!我们打开Array Iterator{}内层:

image.png

发现里面隐式继承了next属性!我们来使用一下!

image.png

我们可以发现返回的迭代器对象内的next属性可以取到数组内的值,next属性里的value代表取到的值,done代表是否还可以next,false代表能继续next,true代表不能next,已经到底了。

字节面试题

OK,清楚了解这些后,重头戏来了!来自字节面试题的一个问题!

题目:如下所示,想让你实现输出a,b。了解解构赋值的会知道数组和对象之间直接解构是不可行的。那怎么解决呢?

let [a,b]={a:1,b:2}

console.log(a,b);//TypeError: {(intermediate value)(intermediate value)} is not iterable

解决方案:

思路: 我们知道第一行等号左边的数组[a,b]可迭代的,右边的对象{a:1,b:2}不可迭代的。那么我们应该‘使’命的想办法让右边的对象变成是可迭代的。既然对象身上没有迭代器属性,那我们就给它一个!

实现: 我们给对象原型上加一个Symbol.iterator属性,以此让对象具有可迭代属性。

Object.prototype[Symbol.iterator] = function(){//Symbol.iterator属性返回的是函数
  return []  //此处应该返回的是一个迭代器对象,不是[],直接{}也不可行
}
let [a,b]={a:1,b:2}

console.log(a,b); //TypeError: undefined is not a function

我们可以看到报的错不再是not iterable,而是undefined is not a function!这是因为Symbol.iteratorreturn出来的一个迭代器对象,所以这样也是不可行的。

再来,我们想数组的解构只能往数组解构,那么我们把对象转成数组,就是硬生生的把对象的值转为[1,2],也就是把值抠出来不要key,那么此时才能解构成立。所以我们现在想让对象的迭代器属性把对象变成[1,2]。利用对象Object身上的values()方法!答案揭晓!

Object.prototype[Symbol.iterator] = function(){//Symbol.iterator属性返回的是函数
  //返回一个Array类型的可迭代对象
  return Object.values(this)[Symbol.iterator]() //this指向实例对象,Object.values(this)得到的是数组
}
let [a,b]={a:1,b:2} //实例对象 相当于获得[1,2]

console.log(a,b); //1 2

tips:Object.values()---该方法返回一个给定对象自身的所有可枚举属性值数组

总结

  • for...of通常用来遍历Symbol.iterator 属性的数据结构,比如数组
  • Iterator 接口主要供for...of消费
  • 默认的 Iterator 接口部署在数据结构的Symbol.iterator属性。一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”。

本篇文章就到此为止啦,由于本人经验水平有限,难免会有纰漏,对此欢迎指正。如觉得本文对你有帮助的话,欢迎点赞收藏❤❤❤,您的点赞是持续写作的动力,感谢支持。