| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 |
1x
1x
37x
37x
37x
7x
1x
1x
10x
2x
8x
5x
5x
10x
16x
10x
1x
1x
1x
1x
20x
1x
1x
1x
3x
1x
2x
1x
44x
1x
18x
18x
18x
18x
14x
14x
1x
1x
4x
1x
1x
1x
3x
3x
1x
11x
11x
1x
1x
1x
1x
3x
3x
1x
3x
1x
1x
2x
1x
20x
1x
16x
2x
1x
14x
2x
1x
12x
22x
6x
| /**
* 纯内存缓存
*
* @module libs/storage/Storage
* @createdAt 2016-07-21
*
* @copyright Copyright (c) 2016 Zhonglei Qiu
* @license Licensed under the MIT license.
*/
var extend = require('../lang/classExtend').extend
var hasOwnProp = require('../lang/hasOwnProp')
/**
* @class
* @param {Object} opts 配置项,具体配置由具体子类决定
* @param {Number} opts.format 指定 toString 调用 JSON.stringify 时的格式
* @param {Boolean} opts.autoInit 在 constructor 内部调用 this.initSync 方法
*
* @example
*
* Storage.extend({
*
* // 注意,不能写成 constructor() { },这种写法
* // 等价于 constructor: function constructor () {}
* // 这样会导致无法使用 new 来创建新生成的类
*
* constructor: function () {
* Storage.apply(this, arguments)
* },
*
* init: function (data) {}
* initSync: function (data) {}
* update: function () {}
* updateSync: function () {}
* })
*
*/
function Storage(opts) {
/**
* 内存中的所有数据
* @type {Object}
*/
this.data = null
/**
* 选项
* @type {Object}
*/
this.opts = Object(opts)
if (this.opts.autoInit) {
this.initSync()
}
}
/**
* @borrows module:libs/lang/classExtend~Base.extend
* @type {Function}
*/
Storage.extend = extend
/**
* 对指定的 key 做 defineProperty 操作,之后就可以通过
* storage.key 或 storage.key = 'xx' 的形式来对 stroage
* 中的数据进行修改。
*
* 主要是采用了 Storage.getSync 和 Storage.setSync 方法
*
* @param {Array<String>|String} [keys] 要操作的 keys,如果不指定,则会对 data 中的所有 keys 处理
* @param {Boolean} silent 是否禁止输出提醒信息(主要是 defineProperty 会出现 key 已经存在的问题)
* @return {Storage}
*/
Storage.prototype.define = function(keys, silent) {
if (typeof keys === 'string') {
keys = [keys]
} else if (!Array.isArray(keys)) {
silent = !!keys
keys = Object.keys(this.data)
}
keys.forEach(function(key) {
_define(this, key, silent)
}, this)
return this
}
/**
* 初始化 Storage
* @return {Promise}
*/
Storage.prototype.init = function(data) {
this.data = Object(data)
return Promise.resolve()
}
/**
* Stroage#init 的同步版本
*/
Storage.prototype.initSync = function(data) {
this.data = Object(data)
}
/**
* 判断 key 是否存在
* @param {String} key
* @return {Promise}
*/
Storage.prototype.has = function(key) {
return Promise.resolve(this.hasSync(key))
}
/**
* Storage.has 的同步版本
* @param {String} key
* @return {*}
*/
Storage.prototype.hasSync = function(key) {
return hasOwnProp(this.data, key)
}
/**
* 获取指定的 key 的值
* @param {String} key
* @return {Promise}
*/
Storage.prototype.get = function(key) {
return Promise.resolve(this.getSync(key))
}
/**
* Storage.get 的同步版本
* @param {String} key
* @return {*}
*/
Storage.prototype.getSync = function(key) {
return this.data[key]
}
/**
* 安全的操作 key,如果操作 key 失败,会将 key 上的值回滚到操作之前的值
* @param {String} key
* @param {Function} op 操作函数
* @param {Boolean} sync 是否是同步操作
* @return {*} 操作函数返回的内容
*/
Storage.prototype.safeOperate = function(key, op, sync) {
var data = this.data
var oldVal = data[key]
op.call(this, data, key)
if (sync) {
try {
return this.updateSync()
} catch (e) {
data[key] = oldVal
throw e
}
} else {
return this.update()
.catch(function(e) {
data[key] = oldVal
return Promise.reject(e)
})
}
}
/**
* 设定值
* @param {String} key
* @param {*} val
* @return {Promise}
*/
Storage.prototype.set = function(key, val) {
return this.safeOperate(key, function(data, key) {
data[key] = val
})
}
/**
* Storage.set 的同步版本
* @param {String} key
* @param {*} val
*/
Storage.prototype.setSync = function(key, val) {
return this.safeOperate(key, function(data, key) {
data[key] = val
}, true)
}
/**
* 删除某个 key
* @param {String} key
* @return {Promise}
*/
Storage.prototype.del = function(key) {
return this.safeOperate(key, function(data, key) {
delete data[key]
})
}
/**
* Storage.del 的同步版本
* @param {String} key
*/
Storage.prototype.delSync = function(key) {
return this.safeOperate(key, function(data, key) {
delete data[key]
}, true)
}
/**
* 将数据同步到对应的存储中
* @return {Promise}
*/
Storage.prototype.update = function() {
return Promise.resolve()
}
/**
* Storage.update 的同步版本
* @return {Promise}
*/
Storage.prototype.updateSync = function() {}
/**
* 将 Storage 中的 data 克隆
* @return {*}
*/
Storage.prototype.toJSON = function() {
return JSON.parse(this.toString())
}
/**
* 将 Storage 中的 data 转化成字符串
* @return {String}
*/
Storage.prototype.toString = function() {
return JSON.stringify(this.data, null, this.opts.format)
}
module.exports = Storage
// 不能取名为 define
// webpack 认为 define 是 全局变量,AMD 模块有 define('xxx', function) 写法
function _define(target, key, silent) {
if (key in target.constructor.prototype) {
if (!silent) {
console.warn('WARN: ignore defineProperty "'
+ key + '", because it is Storage\'s native method.')
}
} else if (key in target) {
if (!silent) {
console.warn('INFO: ignore defineProperty "'
+ key + '", because the property already exists.')
}
} else {
Object.defineProperty(target, key, {
get: function() { return target.getSync(key) },
set: function(v) { return target.setSync(key, v) }
})
}
}
|