面向對象編程: 原生Javascript實現一個支持過期時間的DAO庫
本文主要解決原生localStorage無法設置過期時間的問題,并通過封裝,來實現一個操作便捷,功能強大的localStorage庫,關于庫封裝的一些基本思路和模式,我將采用之前寫的如何用不到200行代碼寫一款屬于自己的js類庫中類似的方法,感興趣的朋友可以學習,交流。
設計思路

我們將基于localStorage原始api進行擴展,讓其支持失效時間,操作完成后的回調。在文章的最后,我將給出庫的完成代碼,接下來我們就一步步實現吧。
正文
首先,我們來設計庫的基本框架:
- const BaseStorage = function(preId, timeSign){
- // 初始化一些操作
- }
- BaseStorage.prototype = {
- storage: localStorage || window.localStorage,
- set: function(key, value, cb, time){
- },
- get: function(key, cb){
- },
- // 刪除storage,如果刪除成功,返回刪除的內容
- remove: function(key, cb){
- }
- }
如上可以發現,我們的storage會有三個核心api,分別為set,get,remove,我們使用localStorage作為基礎庫支持,當然你也可以將上面的庫換成sessionStorage或者其他。
有了基本骨架,我們就可以實現基本功能的封裝,這里我們先在原型中加一個屬性,來列出數據操作中的各個狀態。
- status: {
- SUCCESS: 0, // 成功
- FAILURE: 1, // 失敗
- OVERFLOW: 2, // 數據溢出
- TIMEOUT: 3 // 超時
- },
為了實現過期時間,我們有兩種思路,第一種是先將一個過期時間存到storage中,每次操作都檢查一遍是否過期,但是這種方案意味著對不同的鍵就要設置不同的過期時間的storage與之對應,這樣會占用額外的庫內存,維護起來也不方便。另一種方法就是將過期時間存放到鍵值中,將時間和值通過標識符分隔,每次取的時候從值中截取過期時間,再將真實的值取出來返回,這種方案不會添加額外的鍵值對存儲,維護起來也相對簡單,所以我們采用這種方案。 為了區分不同的庫對象,我們還可以添加鍵前綴,如下:
- const BaseLocalStorage = function(preId, timeSign){
- this.preId = preId; // 鍵前綴
- this.timeSign = timeSign || '|-|'; // 過期時間和值的分隔符
- }
基于這個思想,我們就可以接下來的實現了。
getKey——修飾key的方法,不影響用戶對真實key的影響
- getKey: function(key){
- return this.preId + key
- },
set實現
- set: function(key, value, cb, time){
- var status = this.status.SUCCESS,
- key = this.getKey(key);
- // 設置失效時間,未設置時間默認為一個月
- try{
- time = new Date(time).getTime() || time.getTime();
- }catch(e){
- time = new Date().getTime() + 1000*60*60*24*31
- }
- try{
- this.storage.setItem(key, time + this.timeSign + value);
- }catch(e){
- status = this.status.OVERFLOW;
- }
- // 操作完成后的回調
- cb && cb.call(this, status, key, value)
- }
get實現
- get: function(key, cb){
- var status = this.status.SUCCESS,
- key = this.getKey(key),
- value = null,
- timeSignLen = this.timeSign.length,
- that = this,
- index,
- time,
- result;
- try{
- value = that.storage.getItem(key);
- }catch(e){
- result = {
- status: that.status.FAILURE,
- value: null
- }
- cb && cb.call(this, result.status, result.value);
- return result
- }
- if(value) {
- index = value.indexOf(that.timeSign);
- time = +value.slice(0, index);
- // 判斷是否過期,過期則清除
- if(time > new Date().getTime() || time == 0){
- value = value.slice(index+timeSignLen);
- }else{
- value = null,
- status = that.status.TIMEOUT;
- that.remove(key);
- }
- }else{
- status = that.status.FAILURE;
- }
- result = {
- status: status,
- value: value
- };
- cb && cb.call(this, result.status, result.value);
- return result
- }
remove實現
- // 刪除storage,如果刪除成功,返回刪除的內容
- remove: function(key, cb){
- var status = this.status.FAILURE,
- key = this.getKey(key),
- value = null;
- try{
- value = this.storage.getItem(key);
- }catch(e){
- // dosomething
- }
- if(value){
- try{
- this.storage.removeItem(key);
- status = this.status.SUCCESS;
- }catch(e){
- // dosomething
- }
- }
- cb && cb.call(this, status, status > 0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length))
- }
在api的實現過程中,由于某種誤操作很可能導致storage報錯,所以建議最好用trycatch包裹,這樣可以避免影響后面的邏輯。
接下來我們可以這么使用:
- let a = new BaseStorage('_', '@');
- a.set('name', '123')
- a.get('name') // {status: 0, value: "123"}
- // 設置失效時間
- a.set('name', '123', null, new Date().getTime() + 1000*60*60*24*31)
- // 移除
- a.remove('name')
完整源碼
- /**
- * 數據管理器
- */
- (function(win){
- const BaseStorage = function(preId, timeSign){
- this.preId = preId;
- this.timeSign = timeSign || '|-|';
- }
- BaseStorage.prototype = {
- status: {
- SUCCESS: 0,
- FAILURE: 1,
- OVERFLOW: 2,
- TIMEOUT: 3
- },
- storage: localStorage || window.localStorage,
- getKey: function(key){
- return this.preId + key
- },
- set: function(key, value, cb, time){
- var status = this.status.SUCCESS,
- key = this.getKey(key);
- // 設置失效時間,未設置時間默認為一個月
- try{
- time = new Date(time).getTime() || time.getTime();
- }catch(e){
- time = new Date().getTime() + 1000*60*60*24*31
- }
- try{
- this.storage.setItem(key, time + this.timeSign + value);
- }catch(e){
- status = this.status.OVERFLOW;
- }
- cb && cb.call(this, status, key, value)
- },
- get: function(key, cb){
- var status = this.status.SUCCESS,
- key = this.getKey(key),
- value = null,
- timeSignLen = this.timeSign.length,
- that = this,
- index,
- time,
- result;
- try{
- value = that.storage.getItem(key);
- }catch(e){
- result = {
- status: that.status.FAILURE,
- value: null
- }
- cb && cb.call(this, result.status, result.value);
- return result
- }
- if(value) {
- index = value.indexOf(that.timeSign);
- time = +value.slice(0, index);
- if(time > new Date().getTime() || time == 0){
- value = value.slice(index+timeSignLen);
- }else{
- value = null,
- status = that.status.TIMEOUT;
- that.remove(key);
- }
- }else{
- status = that.status.FAILURE;
- }
- result = {
- status: status,
- value: value
- };
- cb && cb.call(this, result.status, result.value);
- return result
- },
- // 刪除storage,如果刪除成功,返回刪除的內容
- remove: function(key, cb){
- var status = this.status.FAILURE,
- key = this.getKey(key),
- value = null;
- try{
- value = this.storage.getItem(key);
- }catch(e){
- // dosomething
- }
- if(value){
- try{
- this.storage.removeItem(key);
- status = this.status.SUCCESS;
- }catch(e){
- // dosomething
- }
- }
- cb && cb.call(this, status, status > 0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length))
- }
- }
- win.BS = BaseStorage;
- })(window)