javascrpit面向對象之綜合 這一章是對前幾章的一個總結,通過一個案例來綜合認識javascript面向對象的基本用法 需求: 幾乎所有的web應用都需要保存數據一些到本地,那麼我們就來做一個數據儲存器吧。 詳細需求需求: 當本地儲存有數據時,取用本地的數據,沒有時使用預設的數據 判斷本地的數 ...
javascrpit面向對象之綜合 這一章是對前幾章的一個總結,通過一個案例來綜合認識javascript面向對象的基本用法 需求: 幾乎所有的web應用都需要保存數據一些到本地,那麼我們就來做一個數據儲存器吧。 詳細需求需求: 當本地儲存有數據時,取用本地的數據,沒有時使用預設的數據 判斷本地的數據是否過時,如果過時則不使用 預設使用localStorage,但支持使用其它儲存方式,並且要支持多方儲存,多方讀取
抽象出對象 根據需求裡面的關鍵字,我們抽象出三個對象:數據訪問、數據、儲存器 數據儲存管理器負責管理數據,對外暴露介面 數據對象負責對數據的操作 儲存器負責保持數據,讀取數據 儲存器對象 DataStorageManagerBase暴露兩個介面save()和load(),模擬抽象類,告訴子類一定要實現這兩個方法。 下麵的例子用LocalStorage實現了一個子類,當然你也可以用cookie或其它方式實現。 為什麼要把LocalStorage這些儲存器進行二次封裝呢?直接用不就可以了嗎? 因為各種儲存器等api都是不一樣等,我們二次封裝後可以確保無論什麼儲存器對外暴露的介面都是save()和load()。
/*模擬數據儲存器抽象類,其實這個類不要也可以*/ class DataStorageManagerBase { static getIns() { /* 儲存器在整個應用的生命周期里應該只有一個實例 */ if (!this._ins) this._ins = new this(); return this._ins; } constructor() { this.name = null; } save(name/* string */, data/* any */) { throw '"' + this.constructor.name + "'類沒有save()方法"; } load(name/* string */) { throw '"' + this.constructor.name + "'類沒有load()方法"; } } class LocalStorageManager extends DataStorageManagerBase { static getIns() { /* 靜態方法不能繼承 */ if (!this._ins) this._ins = new this(); return this._ins; } constructor() { super(); this.name = 'localStorage'; } save(name/* string */, data/* any */) { console.log(name,data) if (!window.localStorage) return this;//判斷這個儲存器可用不可用,你也可以在這裡拋出錯誤 window.localStorage[name] = JSON.stringify(data); return this; } load(name/* string */) { //如果儲存器不可用,返回false if (!window.localStorage) return false; //如果沒有這個數據,返回false if (!window.localStorage[name]) return false; let dataLoaded = JSON.parse(window.localStorage[name]); return dataLoaded; } }數據對象 對數據的操作:保存、讀取、判斷版本等
class GlobalData { static addStorage(storage/* DataStorageManagerBase */) { /*動態添加儲存器*/ this._storages.push(); } static getStorages() { return this._storages; } constructor(name, data, version) { this.name = name; this.data = data; this.version = version || 1; this._loadData(); //初始化的該對象的時候,讀取localStorage里的數據,看有沒有已儲存的數據,有就用該數據 } getData() { return this._copy(this.data); } setData(data, notSave) { this.data = this._copy(data); if (!!notSave) return this; let dataToSave = { name: this.name, version: this.version, data: this.data }; let storages = GlobalData.getStorages(); for (let i = 0, l = storages.length; i < l; i++) { /*輪詢所有儲存器,把數據保存在這些儲存器中*/ storages[i].save(this.name,dataToSave); } return this; } _copy(data) { /*深拷貝*/ if (typeof data != "object") return data; return JSON.parse(JSON.stringify(data)); } _loadData() { let storages = GlobalData.getStorages(); for (let i = 0, l = storages.length; i < l; i++) { /*輪詢所有儲存器,依次獲取數據*/ const dataLoaded = storages[i].load(this.name); if(!!dataLoaded) { this._updateData(dataLoaded); return; } } } _updateData(dataLoaded) { if (dataLoaded.version < this.version) return this; this.data = dataLoaded.data; return this; } } GlobalData._storages = [LocalStorageManager.getIns()];// 預設添加LocalStorageManager儲存器數據訪問對象 對數據對象管理,對外暴露三個介面getData(),setData(),config(),用戶通過這三個介面使用這個模塊
class GlobalDataDao { static getIns() { if (!this._ins) this._ins = new this(); return this._ins; } constructor() { this.GlobalDataClass = GlobalData; this._dataList = []; } getData(name/* string */) { let dataIns = this.getDataIns(name); if (!!dataIns) { return dataIns.getData(); } else { return null; } } setData(name/* string */, data/* any */, notSave = false/* ?boolean */) { let dataIns = this.getDataIns(name); dataIns.setData(data, notSave); return this; } config(configs/* Array<Config> */) { /* 初始化數據 interface Config { name: string; data; any; version?: number; } */ for (let i in configs) { let de = configs[i]; if (!!this.getDataIns(de.name)) { /* 如果數據名重覆,拋出錯誤 */ throw new Error('data name "' + de.name + '" is exist'); }; let dataIns = new GlobalData(de.name, de.data, de.version); this._dataList.push(dataIns); } return this; } getDataIns(name/* string */) { for (let i in this._dataList) { if (this._dataList[i].name === name) { return this._dataList[i]; } } return false; } }
使用
/*用法*/ let globalDataManeger = GlobalDataDao.getIns(); let configs = [ { name: 'languageUsing', version: 1, data: { name: '簡體中文', value: 'zh-cn' } }, { name: 'userPreference', version: 1, data: { theme: 'blue', menu: 'side_bar' } } ]; globalDataManeger.config(configs); console.log(globalDataManeger); let languageUsing = globalDataManeger.getData('languageUsing'); console.log(languageUsing); languageUsing.name = 'English'; languageUsing.value = 'en'; globalDataManeger.setData('languageUsing', languageUsing);