使用typescript进行了二次封装,"typescript": "5.2.2"。下有编译好的js代码。
目录
应用场景
同源下多个应用进行存储隔离
一、ts代码
"use strict";
// @ts-check
/**
* @description: 装饰器:检查函数入参类型
* @param {string | string[]} type 基本数据类型 || 基本数据类型集合
*/
function checkType(type: string | string[]) {
return function (value: Function, { kind, name }) {
return function (...args: any[]) {
if (Array.isArray(type)) {
args.forEach((item, index) => {
if (typeof item !== type[index]) {
throw new Error(`${kind}: ${name} params[${index}] is not ${type[index]}`);
}
})
} else {
args.forEach((item, index) => {
if (typeof item !== type) {
throw new Error(`${kind}: ${name} params[${index}] is not ${type}`);
}
})
}
return value.call(this, ...args);
};
}
}
/**
* @description: 装饰器:限制参数长度
* @param {number} extent 必传参数长度
*/
function limit(extent: number) {
return function (value: Function, { kind, name }) {
return function (...args: any[]) {
if (args.length < extent) {
throw new Error(`${kind}: ${name} Expect ${extent} parameters, actual ${args.length}`);
}
return value.call(this, ...args);
};
}
}
abstract class Base {
public original: Storage;
protected prefix: string;
constructor(original: Storage, prefix: string) {
this.original = original;
this.prefix = prefix;
}
protected getKey(key: string): string {
return `${this.prefix}_${key}`;
}
}
class WebStorage extends Base {
constructor(original: Storage, prefix: string) {
super(original, prefix);
}
@limit(1)
@checkType("string")
public getItem(key: string): string | null {
const data = this.original.getItem(this.getKey(key));
if (data === null) return null;
try {
const { value, indate } = JSON.parse(data);
if (indate === null) return value;
if (indate >= Date.now()) {
return value;
} else {
this.removeItem(key);
return null;
}
} catch (error) {
return data;
}
}
/**
* @description: 调用存储
* @param {string} key 键
* @param {string} value 值
* @param {timestamp} indate 时间戳-键值有效时间
*/
@limit(2)
@checkType(["string", "string", "number"])
public setItem(key: string, value: string, indate?: number): void {
this.original.setItem(
this.getKey(key),
JSON.stringify({
value: value,
indate: indate ? Date.now() + indate : null
})
);
}
@limit(1)
@checkType("string")
public removeItem(key: string): void {
this.original.removeItem(this.getKey(key));
}
public clear(): void {
this.original.clear();
}
}
export default WebStorage;
代码不过多解释,不复杂,还有注释。
使用了新版ts装饰器,这也是我第一次尝试,据说性能更棒。
二、编译js
1.代码
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
// @ts-check
/**
* @description: 装饰器:检查函数入参类型
* @param {string | string[]} type 基本数据类型 || 基本数据类型集合
*/
function checkType(type) {
return function (value, _a) {
var kind = _a.kind, name = _a.name;
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (Array.isArray(type)) {
args.forEach(function (item, index) {
if (typeof item !== type[index]) {
throw new Error("".concat(kind, ": ").concat(name, " params[").concat(index, "] is not ").concat(type[index]));
}
});
}
else {
args.forEach(function (item, index) {
if (typeof item !== type) {
throw new Error("".concat(kind, ": ").concat(name, " params[").concat(index, "] is not ").concat(type));
}
});
}
return value.call.apply(value, __spreadArray([this], args, false));
};
};
}
/**
* @description: 装饰器:限制参数长度
* @param {number} extent 必传参数长度
*/
function limit(extent) {
return function (value, _a) {
var kind = _a.kind, name = _a.name;
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
if (args.length < extent) {
throw new Error("".concat(kind, ": ").concat(name, " Expect ").concat(extent, " parameters, actual ").concat(args.length));
}
return value.call.apply(value, __spreadArray([this], args, false));
};
};
}
var Base = /** @class */ (function () {
function Base(original, prefix) {
this.original = original;
this.prefix = prefix;
}
Base.prototype.getKey = function (key) {
return "".concat(this.prefix, "_").concat(key);
};
return Base;
}());
var WebStorage = function () {
var _a;
var _classSuper = Base;
var _instanceExtraInitializers = [];
var _getItem_decorators;
var _setItem_decorators;
var _removeItem_decorators;
return _a = /** @class */ (function (_super) {
__extends(WebStorage, _super);
function WebStorage(original, prefix) {
var _this = _super.call(this, original, prefix) || this;
__runInitializers(_this, _instanceExtraInitializers);
return _this;
}
WebStorage.prototype.getItem = function (key) {
var data = this.original.getItem(this.getKey(key));
if (data === null)
return null;
try {
var _b = JSON.parse(data), value = _b.value, indate = _b.indate;
if (indate === null)
return value;
if (indate >= Date.now()) {
return value;
}
else {
this.removeItem(key);
return null;
}
}
catch (error) {
return data;
}
};
/**
* @description: 调用存储
* @param {string} key 键
* @param {string} value 值
* @param {timestamp} indate 时间戳-键值有效时间
*/
WebStorage.prototype.setItem = function (key, value, indate) {
this.original.setItem(this.getKey(key), JSON.stringify({
value: value,
indate: indate ? Date.now() + indate : null
}));
};
WebStorage.prototype.removeItem = function (key) {
this.original.removeItem(this.getKey(key));
};
WebStorage.prototype.clear = function () {
this.original.clear();
};
return WebStorage;
}(_classSuper)),
(function () {
var _b;
var _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_b = _classSuper[Symbol.metadata]) !== null && _b !== void 0 ? _b : null) : void 0;
_getItem_decorators = [limit(1), checkType("string")];
_setItem_decorators = [limit(2), checkType(["string", "string", "number"])];
_removeItem_decorators = [limit(1), checkType("string")];
__esDecorate(_a, null, _getItem_decorators, { kind: "method", name: "getItem", static: false, private: false, access: { has: function (obj) { return "getItem" in obj; }, get: function (obj) { return obj.getItem; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _setItem_decorators, { kind: "method", name: "setItem", static: false, private: false, access: { has: function (obj) { return "setItem" in obj; }, get: function (obj) { return obj.setItem; } }, metadata: _metadata }, null, _instanceExtraInitializers);
__esDecorate(_a, null, _removeItem_decorators, { kind: "method", name: "removeItem", static: false, private: false, access: { has: function (obj) { return "removeItem" in obj; }, get: function (obj) { return obj.removeItem; } }, metadata: _metadata }, null, _instanceExtraInitializers);
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})(),
_a;
}();
exports.default = WebStorage;
2.使用方式
import WebStorage from "./webStorage.js";
const prefix = "projectName";
const customStorage = {
localStorage: new WebStorage(window.localStorage, prefix),
sessionStorage: new WebStorage(window.sessionStorage, prefix)
};
customStorage.localStorage.setItem("test1", "a");
console.log(customStorage.localStorage.getItem("test1"));
customStorage.localStorage.setItem("test2", "b", 3000);
console.log(customStorage.localStorage.getItem("test2"));
setTimeout(() => {
console.log(customStorage.localStorage.getItem("test2"))
}, 5000);
基本对标原生态的使用方式,传参类型更严格,新增了存储过期的能力,使用无过多心智开销。 项目支持ts就用ts,仅支持js就用编译好的js,一样滴。使用方式建议挂载在一个公共的对象上,进行向下分发,不进行多次初始化。
总结
觉得写的有问题的地方可以自己修改ts代码,重新使用typescript编译成js,欢迎大佬们评论区指点错误。如果有更好应对该场景的解决方案,留下链接让我学习一下。😁