es6:解构和迭代器的渊源

236 阅读4分钟

什么是解构?

在聊他俩之间的故事前,我们先来聊聊今天的主人公解构,这个方法是es6中新增的一种方便的语法,可以让我们从对象或者数组中快速的获取值,并且将值赋予给某个变量,下面给大家展示一下。

在解构之前我们如果想要获取数组中的值会干下面的操作:

const arr = [1, 2]
let a = arr[0], b = arr[1]
console.log(a, b);

这样的操作让我们会写很多的重复代码未免有些复杂,接下来我们来看看解构出现之后的写法:

tips: 对象解构出来的变量名要与对象中存在的属性名一样

// 数组解构
const arr = [1, 2]
let [a, b] = arr
console.log(a, b);


// 对象解构
const obj = {
  c: 3,
  d: 4
}
let  { c, d } = obj
console.log(c, d);

对象能解构成数组吗?

在了解完了解构的基本用法之后可能就会有同学有疑惑了,难道对象只能解构成对象,数组只能解构成数组吗,如果我把对象解构成数组会怎么样?这是一个非常好的问题,下面我们来控制台打印输出看看:

image.png

我们可以看到浏览器的控制台错误提示:这不是一个可迭代对象,这也就说明解构数组只能对可迭代对象进行解构,接下来我们来聊聊可迭代对象是什么和迭代器。

可迭代对象是什么呢?

简单来说就是要满足可迭代协议的对象就是一个可迭代对象

可迭代协议:一个对象中有一个属性它的名称是[Symbol.iterator],并且这个属性值是一个函数,如果满足则是一个可迭代对象

迭代器简介

迭代器是某些数据结构的属性,并不是方法。可以被遍历的数据结构就会有迭代器属性,例如数组、Map和Set等,但是对象没有自带的迭代器属性。那这个迭代器属性是什么,又在哪里呢?我们来浏览器的控制台打印看看数组的迭代器放在哪里并且它是个什么东西:

image.png

image.png

1746090470226.png

我们可以一直往下面翻,直到找到数组中的一个Symbol.iterator属性,看这个属性的名字想必各位都看出点端倪了,这个iterator不就是迭代器吗?是也不全是,它确实是我们要找的那个属性,并且它还是一个函数,但是它返回出来的东西才是我们所要的那个迭代器,下面我们来看看这个属性的返回值是什么:

image.png

我们可以看到控制台打印结果返回了一个迭代器对象,而这就是我们要找的那个迭代器,那这个迭代器有什么作用呢?它主要是通过它里面的next()方法来发挥它的作用,接下来我们来看看它的next()方法是干啥的:

image.png

next() 方法会返回一个对象,其中包含两个值 valuedone

  • value:存放当前遍历到的值
  • done:是否已经遍历完成

数组解构是如何完成的

当了解完了上文的迭代器是什么之后,想必大家对数组解构可能心中也有了一个模糊的猜测:我们之所以能对数组进行解构是因为它是一个可迭代对象所以才能对数组进行解构。这个猜测是正确的,数组的解构本质上就是一个语法糖,它做的事情呢就是通过迭代将值赋予给你想要赋值的变量,我们可以大致将其看为下面的代码:

image.png

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

想要实现对象解构成数组当前看是不行的,但是我们通过上文对解构和迭代器的了解之后,想必大家都有了一个思路:它之所以不能实现,是因为对象不是一个可迭代对象,它里面没有Symbol.iterator属性,所以我们要想实现对象解构成数组,首先得在它身上挂载这么一个属性,并且将其赋值为一个函数:

Object.prototype[Symbol.iterator] = function () {
  
}

接着我们需要返回一个迭代器,而这个迭代器在数组身上就有,我们只需要调用数组上面[Symbol.iterator]属性的函数就能得到一个迭代器:

Object.prototype[Symbol.iterator] = function () {
  const arr = [1, 2]
  return arr[Symbol.iterator]()
}

这时候我们就有点小缺陷了,这样的话每次前面的变更我函数内部的内容不也得变更,所以我们直接通过this来指向要调用这个方法的对象即可:

Object.prototype[Symbol.iterator] = function () {
  return Object.values(this)[Symbol.iterator]()
}