从零到有模拟实现一个Set类

原创 我的小苹果 随笔 javascript 311阅读 2018-09-14 23:51:41 举报

前言

es6新增了Set数据结构,它允许你存储任何类型的唯一值,无论是原始值还是对象引用。这篇文章希望通过模拟实现一个Set来增加对它的理解。

原文链接

用在前面

实际工作和学习过程中,你可能也经常用Set来对数组做去重处理

基本语法

以下内容基本出自MDN,这里写出来,纯粹是为了便于后面的模拟操作。如果你已经很熟悉了,可以直接略过。

可以传递一个可迭代对象,它的所有元素将被添加到新的 Set中。如果不指定此参数或其值为null,则新的 Set为空。

实例属性和方法

属性

constructor Set的构造函数

size Set 长度

操作方法

  1. Set.prototype.add(value)
    在Set对象尾部添加一个元素。返回该Set对象。
  2. Set.prototype.has(value)
    返回一个布尔值,表示该值在Set中存在与否。
  3. Set.prototype.delete(value)
    移除Set中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值(即如果该元素存在,返回true,否则返回false)
  4. Set.prototype.clear()
    移除Set对象内的所有元素。没有返回值

栗子

遍历方法

  1. Set.prototype.keys()
    返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
  2. Set.prototype.values()
    返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
  3. Set.prototype.entries()
    返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值的[value, value]数组。为了使这个方法和Map对象保持相似, 每个值的键和值相等。
  4. Set.prototype.forEach(callbackFn[, thisArg])
    按照插入顺序,为Set对象中的每一个值调用一次callBackFn。如果提供了thisArg参数,回调中的this会是这个参数。

栗子

整体结构

以上回顾了一下Set的基本使用,我们可以开始尝试模拟实现一把啦。你也可以直接点击查看源码。

目录结构

├──set-polyfill
│ ├──iterator.js // 导出一个构造函数Iterator,模拟创建可迭代对象
│ ├──set.js // Set类
│ ├──utils.js // 辅助函数
│ ├──test.js // 测试

Set整体框架

辅助方法

开始实现Set细节前,我们先看一下会用到的一些辅助方法

  1. assert, 这个方法是学习vuex源码时候看到的,感觉蛮实用的,主要用来对某些条件进行判断,抛出错误。
  1. isDef, 过滤掉nullundefined
  1. isIterable, 简单判断value是否是迭代器对象.
  1. forOf, 模拟for of行为, 对迭代器对象进行遍历操作。

源码实现

测试一把

执行 node test.js

size属性和操作方法

上面的例子把Set的size属性和操作方法过了一遍,打印出来的Set实例和原生的长得不太一样,就先不管了。

遍历方法

遍历方法看起来也可以达到和前面例子一样的效果,源码实现部分基本就到这里啦,但是还没完...

  1. 为什么[ ...s2 ]可以得到数组[ 's', 'e', 't' ]呢?
  2. s2 为什么可以被for of循环呢?

iterator(迭代器)

MDN找来这段话,在JavaScript中迭代器是一个对象,它提供了一个next() 方法,用来返回序列中的下一项。这个方法返回包含两个属性:done(表示遍历是否结束)和 value(当前的值)。

迭代器对象一旦被创建,就可以反复调用next()。

这个时候可以讲一下我们的iterator.js中的代码了

Iterator的实例有一个next方法,每次调用都会返回一个done属性和value属性,其语意和前面的解释是一样的。

看到这里你可能已经知道了,Iterator要实现的功能之一就是提供一个迭代器。那这个又和上面的问题1和2有啥关系呢?我们再来看看for of

for of

一个数据结构只要部署了Symbol.iterator属性,就被视为具有iterator接口,就可以用for...of循环遍历它的成员。也就是说,for...of循环内部调用的是数据结构的Symbol.iterator方法 for...of 循环

默认只有(Array,Map,Set,String,TypedArray,arguments)可被for of迭代。我们自定义的Set类不在这其中,前面的例子中却在for of循环中打印出了想要的值。原因就是我们给Iterator类部署了Symbol.iterator方法,执行该方法便返回Iterator实例本身,它是一个可以被迭代的对象。

到这里上面的问题2就可以解释通了。

再看看问题1为什么[ ...s2 ]可以得到数组[ 's', 'e', 't' ]呢?,原因也是我们给Setkeysvaluesentries部署了Symbol.iterator,使之具有“iterator”接口,而扩展运算符...的特点之一就是任何具有Iterator接口的对象,都可以用扩展运算符转为真正的数组。

结尾

模拟过程中可能会有相应的错误,也不是和原生的实现完全一致。仅当学习之用,欢迎大家拍砖。

原文链接

参考

  1. Set
  2. 迭代器和生成器
  3. ES6 系列之模拟实现一个 Set 数据结构
  4. 展开语法
  5. for...of 循环
评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复