JavaScript面向对象轻松入门之综合

admin2个月前笔记44
需求:

几乎所有的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 */) {
        /* 初始化数据
        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);


相关文章

CentOS 初级安全防护

CentOS 初级安全防护

Centos 初级安全防护使用的 centos7.2,基本策略如下:禁止 root 账号远程登录修改远程 ssh 登录端口开启防火墙 iptables如何禁止 root 账户远程登录,修改远程 ssh...

Nginx配置文件nginx.conf详解和nginx的变量规则

Nginx配置文件nginx.conf详解Nginx 总的 配置文件 位置 /usr/local/nginx/conf/nginx.confnginx 正则匹配一.正则表达式匹配,其中:符号功能~为区...

25个iptables常用示例

本文将给出25个iptables常用规则示例,这些例子为您提供了些基本的模板,您可以根据特定需求对其进行修改调整以达到期望。格式iptables [-t 表名] 选项 [链名] [条件] [-j 控制...

VPN服务器搭建

VPN服务器搭建

各位处在Linux运维潮流中的大佬们,文章不才,在这里特此分享一套VPN服务器搭建的流程及使用分享。 每个公司都或多或少的进行远程办公,有的公司会采用向日葵orTeamviewer也或者是ngrok、...

V2Ray 是什么?

V2Ray 是什么?

V2Ray 是一个网络代理工具,通过使用特定的中转服务器完成数据传输,支持 HTTP(只支持传入)、Socks、Shadowsocks、Trojan、VMess、VLESS等内容传输协议,每个内容传输...

前后端分离的好处有哪些?

前后端分离的好处有哪些?

前后端分离是目前一种非常流行的开发模式,它使项目的分工更加明确,后端负责处理、存储数据;前端负责显示数据.前端和后端开发人员通过接口进行数据的交换。一、什么是前后端分离?前后端分离实质上是前后端代码分...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。