代码要写成别人看不懂的样子(十七)

本篇文章参考书籍《JavaScript设计模式》–张容铭

前言

  各位作为一名前端开发,大家有没有遇到过前端存储,比如用户初次进入程序,会有一个个性化引导,一般来说这块功能是交给后台同事来做的,但是有时候需要放在前端,来保存是否第一次登陆的字段。

  那我们在本地存储的时候呢,可能会遇到很多问题,每添加一组数据,担心会不会覆盖别人的数据等等,本节的设计模式,就介绍怎么合理前端存储。

数据访问对象模式

  抽象和封装对数据源的访问与存储, DAO通过对数据源链接的管理方便对数据的访问与储存

  我们可以创建一个数据访问对象类,来解决上面的问题。这个数据访问对象类是对本地存储的一次封装,要创建 DAO 类,首先要了解当前的需求,最好也顾虑一下其他人的需求,这样创建的 DAO 类才更加健全。

  首先,我们的数据需要有增,删,改,查的功能,其次当别人使用本地存储的时候,也需要知道我保存了哪些数据。

  本地存储使用的是 localStorage 这个对象,对于同一个站点,里面根本没有分割库,所以所有人用的 localStorage 都是同一个,所以应该将每次存储的数据前面字段前面添加字段标识来分割 localStorage 库,

  本地存储对数据的保存实际上是 localStorage 的一个字符串属性,有时对于存储来说,了解它的时间很重要,能方便日后的管理,因此我们还需要一个时间戳。

/**
* 本地存储类
* 参数 preId    本地存储数据库前缀
* 参数 timeSign 时间戳与数据之间的拼接符
*/
var BaseLocalStorage = function(preId, timeSign) {
    
    
	//定义本地存储数据库前缀
	this.preId = preId;
	//定义时间戳与数据之间的拼接符
	this.timeSign = timeSign || '|-|';
}

  为了知道数据的状态,我们可以在 DAO 类内部保存操作返回状态供肉厚使用时调用,我们还需要将本地存储服务的引用保存在 DAO 类的内部以方便我们使用。当然 DAO 类还需要提供对数据库的增删改查操作接口方法。

//本地存储类原型方法
BaseLocalStorage.prototype = {
    
    
	//操作状态
	status: {
    
    
		SUCCESS: 0,   //成功
		FALLURE: 1,   //失败
		OVERFLOW: 2,   //溢出
		TIMEOUT: 3,   //过期
	},
	//保存本地存储链接
	stroge: localStorage || window.localStorage,
	//获取本地存储数据库真实字段
	getKey: function(key) {
    
    
		return this.preId + key;
	},
	//添加(修改)数据
	set: function(key, value, callback, time) {
    
    
		//省略操作
	},
	//获取数据
	get: function(key, callback) {
    
    
		//省略操作
	},
	//删除数据
	remove: function(key, callback) {
    
    
		//省略操作
	}
}

  我们下面的事,只要实现 set get remove 就可以了。首先是 set 方法,我们需要在字段中添加时间戳,我们向本地存储中添加的数据实质上事调用 localStorage setItem 方法。最后我们还要执行回调函数并将操作的结果传入回调函数中。

/**
* 添加(修改)数据
* 参数 key      数据字段标识
* 参数 value    数据值
* 参数 callback 回调函数
* 参数 time     添加时间
*/
set: function(key, value, callback, 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.OVERFKOW;
	}
	//有回调函数则执行回调函数,并传入参数操作状态,真实数据字段标识以及存储数据值
	callback && callback.call(this, status, key, calue);
}

  有了设置数据方法,通过存储字段获取数据的方法也就简单了,还是调用 localStorage getItem 方法,不过需要注意是,这里兼容了四种查询数据的情况,第一种,该字段数据本来就不存在,这样应该返回失败状态;第二种,操作成功但没有获取到值,此时也应该返回失败;第三种,获取到值了,但是时间已经过期了,此时应该删除该数据;最后一种,成功获取数据并成功返回。

/**
* 获取数据
* 参数 key      数据字段标识
* 参数 callback 回调函数
*/
get: function(key, callback) {
    
    
		//默认操作状态时成功
	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) {
    
    
		//获取失败则返回失败状态,数据结果为null
		result = {
    
    
			status: that.status.FAILURE,
			value: null
		};
		//执行回调并返回
		callback && callback.call(this, result.status, result.value);
		return result;
	}
	//如果成功获取数据字符串
	if(value) {
    
    
		//获取时间戳与存储数据之间的拼接符起始位置
		index = value.indexOf(that.timeSign);
		//获取时间戳
		time = +value.slice(0, index);
		//如果时间为过期
		if(new Date(tiem).getTime() > newDate.getTime() || time == 0) {
    
    
			//获取数据结果(拼接符后面的字符串)
			value = value.slice(index + timeSignLen);
		} else {
    
    
			//过期则结果为null
			value = null;
			//设置状态为过期状态
			status = that.status.TIMEOUT;
			//删除该字段
			that.remove(key);
		}
	} else {
    
    
		//未获取数据字符串状态为失败状态
		status = that.status.FAILURE;
	}
	//设置结果
	result = {
    
    
		status: status,
		value: value
	};
	//执行回调函数
	callback && cakkback.call(this, result.status, result.value);
	//返回结果
	return result;
}

   remove 方法照猫画虎,首先删除应该用 localStorage removeItem 方法。执行时会有 3 中状态,第一种,没有修改字段,这样操作结果是失败状态,第二种情况是删除(调用 removeItem 方法) 时发生异常,这样也应该是失败,最后一种当然就是修改成功。

/**
* 删除数据
* 参数 key      数据字段标识
* 参数 callback 回调函数
*/
remove: function(key, callback) {
    
    
	//设置默认操作状态为失败
	var status = this.status.FAILURE,
		//获取实际数据字段名称
		key = this.getKey(key),
		//设置默认数据结果为空
		value = null;
	try {
    
    
		//获取字段对应的数据
		value = this.storage.getItem(key);
	} catch (e) {
    
    }
	//如果数据存在
	if(value) {
    
    
		try {
    
    
			//删除数据
			this.storage.removeItem(key);
			//设置操作成功
			status = this.status.SUCCESS;
		} catch (e) {
    
    }
	}
	//执行回调,注意传入回调函数中的数据值:如果操作状态成功则返回真实的数据结果,否则返回空
	callback && callback.call(this, status, status > 0 ? null : value.slice(value.indexOf(this.timeSign) + this.timeSign.length))
}

  这样我们的整个 DAO类 就完成了,接下来测试一下。上面有一个小技巧不知道大家发现没有,我们在设置默认状态的时候, set get 默认操作是成功,而 remove 的操作状态却是失败,这是为什么的?

  这是因为我们在设置默认状态的时候,应该保证默认状态尽可能多的出现,这样我们的代码执行效率会高一些,这也体现了一个工程师对自己设计的代码的一个全局把控能力。

var LS = new BaseLocalStorage('LS__');
LS.set('a', 'xiao ming', function() {
    
    console.log(arguments);});  //[0, "xiao ming", "LS__a", "xiao ming"]
LS.get('a', function() {
    
    console.log(arguments);});      //[0, "xiao ming"]
LS.remove('a', function() {
    
    console.log(arguments);});   //[0, "xiao ming"]
LS.remove('a', function() {
    
    console.log(arguments);});   //[1, null]
LS.get('a', function() {
    
    console.log(arguments);});      //[1, null]

  对于不支持本地存储的浏览器,我们有时会通过 cookie 或者浏览器 userData 存储区存储数据,这就需要对我们的 DAO 进行重构,兼容更多浏览器,重构方法可以用我们之前学习到的设计模式,各位就看你们的掌握程度了。具体大家可以当作一个练习题来做。




猜你喜欢

转载自blog.csdn.net/EcbJS/article/details/110954579