本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
1. 学习目标
- 学习链表和数组的区别,时间复杂度
- yocto-queue 的适用场景?
- 对 Symbol.iterator 的理解
2. 参考资料
- 任务发布: 【若川视野 x 源码共读】第32期 | yocto-queue 队列 链表
- github1s: github1s.com/sindresorhu…
- npm-yocto-queue: www.npmjs.com/package/yoc…
3. 学习过程
1. 数组和链表的区别
数组和链表是两种不同的数据存储方式。
- 数组是一组具有相同数据类型的变量的集合,这些变量称之为集合的元素,每个元素都有一个编号,称之为下标,可以通过下标来区别并访问数组元素,数组元素的个数叫做数据的长度。
- 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,链表的特性是在中间任意位置插入和删除元素都非常快,不需要移动其它元素。
2. yocto-queue 的适用场景?
You should use this package instead of an array if you do a lot of Array#push() and Array#shift() on large arrays, since Array#shift() has linear time complexity O(n) while Queue#dequeue() has constant time complexity O(1) . That makes a huge difference for large arrays.
A queue is an ordered list of elements where an element is inserted at the end of the queue and is removed from the front of the queue. A queue works based on the first-in, first-out (FIFO) principle.
翻译如下:
你应该使用这个包,当你要对一个大数组做频繁的push和shift操作时,shift方法是线性的时间复杂度的,也就是O(n)而Queue#dequeue方法是固定的时间复杂度,也就是O(1),这时使用队列就很有意义了。
队列就是一个有序的列表,添加元素向队列部尾部添加,移除元素从头部移除,它遵循先进先出的原则,也就是(FIFO)原则。
3. yocto-queue 如何使用
npm install yocto-queue
import Queue from 'yocto-queue';
const queue = new Queue();
queue.enqueue('🦄');
queue.enqueue('🌈');
console.log(queue.size);
//=> 2
console.log(...queue);
//=> '🦄 🌈'
console.log(queue.dequeue());
//=> '🦄'
console.log(queue.dequeue());
//=> '🌈'
4. yocto-queue 的源码
/*
How it works:
`this.#head` is an instance of `Node` which keeps track of its current value and nests another instance of `Node` that keeps the value that comes after it. When a value is provided to `.enqueue()`, the code needs to iterate through `this.#head`, going deeper and deeper to find the last value. However, iterating through every single item is slow. This problem is solved by saving a reference to the last value as `this.#tail` so that it can reference it to add a new value.
*/
class Node {
value;
next;
constructor(value) {
this.value = value;
}
}
export default class Queue {
#head;
#tail;
#size;
constructor() {
this.clear();
}
enqueue(value) {
const node = new Node(value);
if (this.#head) {
this.#tail.next = node;
this.#tail = node;
} else {
this.#head = node;
this.#tail = node;
}
this.#size++;
}
dequeue() {
const current = this.#head;
if (!current) {
return;
}
this.#head = this.#head.next;
this.#size--;
return current.value;
}
clear() {
this.#head = undefined;
this.#tail = undefined;
this.#size = 0;
}
get size() {
return this.#size;
}
* [Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
}
5. 对 Symbol.iterator 的理解
Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被 for...of 循环使用。
在 yocto-queue 源码中,
* [Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
通过上述实现,我们可以通过for...of获取到队列中的值
const queue = new Queue();
queue.enqueue('🦄');
queue.enqueue('🌈');
console.log([...queue]); // 输出:['🦄', '🌈']
总结
先做一个简单的了解,暂时还没有这种场景的需求。
参考笔记: