鸽了好久,最近沉迷游戏,继续写点什么吧,也不知道有没有人看。
其实这个node的源码也不知道该怎么写了,很多模块涉及的东西比较深,JS和C++两头看,中间被工作耽搁回来就一脸懵逼了,所以还是挑一些简单的吧!
这一篇选的是定时器模块,简单讲就是初学者都非常熟悉的setTimeout与setInterval啦,源码的JS内容在目录lib/timers.js中。
node的定时器模块是自己单独实现的,与Chrome的window.setTimeout可能不太一样,但是思想应该都是相通的,学一学总没错。
链表
定时器模块实现中有一个关键数据结构:链表。用JS实现的链表,大体上跟其他语言的链表的原理还是一样,每一个节点内容可分为前指针、后指针、数据。
源码里的链表构造函数有两种,一个是List的容器,一个是容器里的item。
这里看看List:
function TimersList(msecs, unrefed) { // 前指针 this._idleNext = this; // 后指针 this._idlePrev = this; // 数据 this._unrefed = unrefed; this.msecs = msecs; // ...更多}
这是一个很典型的链表例子,包含2个指针(属性)以及数据块。item的构造函数大同小异,也是包含了两个指针,只是数据内容有些不同。
关于链表的操作,放在了一个单独的JS文件中,目录在lib/internal/linkedlist.js,实现跟C++、Java内置的有些许不一样。
看一下增删就差不多了,首先看删:
function remove(item) { // 处理前后节点的指针指向 if (item._idleNext) { item._idleNext._idlePrev = item._idlePrev; } if (item._idlePrev) { item._idlePrev._idleNext = item._idleNext; } // 重置节点自身指针指向 item._idleNext = null; item._idlePrev = null; }
关于数据结构的代码,都是虽然看起来少,但是理解起来都有点恶心,能画出图就差不多了,所以这里给一个简单的示意图。

应该能看懂吧……反正中间那个假设就是item,首先让前后两个对接上,然后把自身的指针置null。
接下来是增。
function append(list, item) { // 先保证传入节点是空白节点 if (item._idleNext || item._idlePrev) { remove(item); } // 处理新节点的头尾链接 item._idleNext = list._idleNext; item._idlePrev = list; // 处理list的前指针指向 list._idleNext._idlePrev = item; list._idleNext = item; }
这里需要注意,初始化的时候就有一个List节点,该节点只作为链表头,与其余item不一样,一开始前后指针均指向自己。

以上是append节点的三步示例图。
之前说过JS实现的链表与C++、Java有些许不一样,就在这里,每一次添加新节点时:
C++/Java:node-node => node-node-new
JS(node):list-node-node => list-new-node-node
总的来说,JS用了一个list来作为链表头,每一次添加节点都是往前面塞,整体来讲是一个双向循环链表。
而在C++/Java中则是可以选择,API丰富多彩,链表类型也分为单向、单向循环、双向等。
setTimeout

