最近有接触到缓存这一块,无意间发现了这个清凉级的缓存,而且也不复杂,刚好作为源码练习一下,戳此观赏源码 -> 点我,点我
话不多说,直接来看看...
先从如何使用来入手,毕竟这是最直观的
- put = function(key, value, time, timeoutCallback)
- 简洁的存储一个value
- 如果没有带入time,这个value会永久存储
- 在给定的time之后会移除value,time是毫秒(via setTimeout)
- timeoutCallback是可选的参数,它会在传入的kye,value过期之后激活
- 返回缓存的value
- get = function(key)
- 在缓存中查找对应key的value
- 如果缓存中没有value,返回null
- del = function(key)
- 删除一个key,返回boolean来判断key是否被删除
- clear = function()
- 删除所有的key
- size = function()
- 返回当前缓存中的缓存数量
- memsize = function()
- 返回当前缓存所占的空间
- 通常
== size()
,除非setTimeout
出了问题
- debug = function(bool)
- 开启/关闭debugging模式
- hits = function()
- 返回缓存命中数(只在debug模式下监听)
- misses = function()
- 返回缓存未命中数(只在debug模式下监听)
- keys = funciton()
- 返回所有缓存中的keys
- exportJson = function()
- 将所有缓存数据转成JSON返回
- 所有timeoutCallbacks将被忽略
- importJson = function(json:string, options:{skipDuplicates:boolean})
- 合并所有在之前的调用中
export
进入缓存的数据 - 在
import
之前的所有已存在的数据都将保存在缓存中 - 当
skipDuplicates
为true
时所有重复的key都将被重写 - 所有数据被暴露后,在导入时都将过期(但是他们的回调不会执行)
- 可用
options
skipDuplicates
:如果为true
,所有重复的keys在导入的时候都会被跳过。默认为false
- 返回新的缓存数量
- 合并所有在之前的调用中
- Cache = function()
- Cache构造器
- 需要注意
require('cache')
会返回Cache的默认实例 require('cache').Cache
才是实际的类。
看了这些api,我们大致知道了用法,而且文档也特别详细。相信对阅读源码很是方便,下面直接上。
作者好久没更新了,不过这并不影响它有900多颗星星:smile:, 文件也很直观,需要源码就在index.js里面。index.js
'use strict';
function Cache () {
var _cache = Object.create(null);
var _hitCount = 0;
var _missCount = 0;
var _size = 0;
var _debug = false;
this.put = function(key, value, time, timeoutCallback) {
if (_debug) {
console.log('caching: %s = %j (@%s)', key, value, time);
}
if (typeof time !== 'undefined' && (typeof time !== 'number' || isNaN(time) || time <= 0)) {
throw new Error('Cache timeout must be a positive number');
} else if (typeof timeoutCallback !== 'undefined' && typeof timeoutCallback !== 'function') {
throw new Error('Cache timeout callback must be a function');
}
var oldRecord = _cache[key];
if (oldRecord) {
clearTimeout(oldRecord.timeout);
} else {
_size++;
}
var record = {
value: value,
expire: time + Date.now()
};
if (!isNaN(record.expire)) {
record.timeout = setTimeout(function() {
_del(key);
if (timeoutCallback) {
timeoutCallback(key, value);
}
}.bind(this), time);
}
_cache[key] = record;
return value;
};
this.del = function(key) {
var canDelete = true;
var oldRecord = _cache[key];
if (oldRecord) {
clearTimeout(oldRecord.timeout);
if (!isNaN(oldRecord.expire) && oldRecord.expire < Date.now()) {
canDelete = false;
}
} else {
canDelete = false;
}
if (canDelete) {
_del(key);
}
return canDelete;
};
function _del(key){
_size--;
delete _cache[key];
}
this.clear = function() {
for (var key in _cache) {
clearTimeout(_cache[key].timeout);
}
_size = 0;
_cache = Object.create(null);
if (_debug) {
_hitCount = 0;
_missCount = 0;
}
};
this.get = function(key) {
var data = _cache[key];
if (typeof data != "undefined") {
if (isNaN(data.expire) || data.expire >= Date.now()) {
if (_debug) _hitCount++;
return data.value;
} else {
// free some space
if (_debug) _missCount++;
_size--;
delete _cache[key];
}
} else if (_debug) {
_missCount++;
}
return null;
};
this.size = function() {
return _size;
};
this.memsize = function() {
var size = 0,
key;
for (key in _cache) {
size++;
}
return size;
};
this.debug = function(bool) {
_debug = bool;
};
this.hits = function() {
return _hitCount;
};
this.misses = function() {
return _missCount;
};
this.keys = function() {
return Object.keys(_cache);
};
this.exportJson = function() {
var plainJsCache = {};
// Discard the `timeout` property.
// Note: JSON doesnt support NaN, so convert it to NaN.
for (var key in _cache) {
var record = _cache[key];
plainJsCache[key] = {
value: record.value,
expire: record.expire || 'NaN',
};
}
return JSON.stringify(plainJsCache);
};
this.importJson = function(jsonToImport, options) {
var cacheToImport = JSON.parse(jsonToImport);
var currTime = Date.now();
var skipDuplicates = options && options.skipDuplicates;
for (var key in cacheToImport) {
if (cacheToImport.hasOwnProperty(key)) {
if (skipDuplicates) {
var existingRecord = _cache[key];
if (existingRecord) {
if (_debug) {
console.log('Skipping duplicate imported key \'%s\'', key);
}
continue;
}
}
var record = cacheToImport[key];
// record.expire could be `'NaN'` if no expiry was set.
// Try to subtract from it; a string minus a number is `NaN`, which is perfectly fine here.
var remainingTime = record.expire - currTime;
if (remainingTime <= 0) {
// Delete any record that might exist with the same key, since this key is expired.
this.del(key);
continue;
}
// Remaining time must now be either positive or `NaN`,
// but `put` will throw an error if we try to give it `NaN`.
remainingTime = remainingTime > 0 ? remainingTime : undefined;
this.put(key, record.value, remainingTime);
}
}
return this.size();
};
}
module.exports = new Cache();
module.exports.Cache = Cache;
复制代码
这样一看,当然没有头绪呀,这么长一段代码。。。 下面拆分一下:
Part 1:
我们弱化了具体实现,总览一下大概。发现其实Cache只是一个函数而已,通过看最后两行
module.exports = new Cache();
module.exports.Cache = Cache;
复制代码
发现这正好是api:Cache = function()
的解释。我们还知道啥?其实这里就可以看出很多东西,Cache里面有_cache, _hitCount, _missCount, _size, _debug这几个变量,不难猜出一些东西:
变量 | 说明 |
---|---|
_cache | 是初始化的一个对象,下面可以使用到 |
_hitCount | 用于缓存命中的计数 |
_missCount | 用户缓存未命中的计数 |
_size | 缓存中的数量 |
_debug | 开启debug模式 |
嗯,除此之外,就是一些属性方法,具体看看实现吧!
Part 2:
下面就看看第一个put方法是做了啥?
- put = function(key, value, time, timeoutCallback)
- 简洁的存储一个value
- 如果没有带入time,这个value会永久存储
- 在给定的time之后会移除value,time是毫秒(via setTimeout)
- timeoutCallback是可选的参数,它会在传入的kye,value过期之后激活
- 返回缓存的value
Part 3:
在看看get方法
- get = function(key)
- 在缓存中查找对应key的value
- 如果缓存中没有value,返回null
Part 4:
剩下的差不多也是和上面类似,就不一一分析啦,直接看:
- exportJson = function()
- 将所有缓存数据转成JSON返回
- 所有timeoutCallbacks将被忽略
- importJson = function(json:string, options:{skipDuplicates:boolean})
- 合并所有在之前的调用中
export
进入缓存的数据 - 在
import
之前的所有已存在的数据都将保存在缓存中 - 当
skipDuplicates
为true
时所有重复的key都将被重写 - 所有数据被暴露后,在导入时都将过期(但是他们的回调不会执行)
- 可用
options
skipDuplicates
:如果为true
,所有重复的keys在导入的时候都会被跳过。默认为false
- 返回新的缓存数量
- 合并所有在之前的调用中
Part 5:
虽然这个node-cache没那么难,写的简洁易懂,也是一种能力呀。就当学习源码的一道开胃菜。