优雅的 JavaScript 之 编写优雅的 Settings
2018-06-04 本文已影响80人
一半晴天
写应用时经常需要用到保存一些用户设置的功能。怎么样在 JavaScript
写出优雅的代码出来呢?下面我将一步步实现出我认为比较优雅的代码。
初始阶段
假设我们一开始封装了如下代码。其中 storage
你可以当作是 LocalStorage
或者就当作小程序中的 storage
。总而言之当作一个 Key Value 存储对象。
interface IStorage {
[key: string]: any;
}
const storage: IStorage = {};
function getByKey(key: string): any {
return storage[key];
}
function setByKey(key: string, value: any) {
if (typeof value === "undefined") {
return;
}
storage[key] = value;
}
添加我们编好设置代码
在上面的代码的基础上,此时我们需要记录用户的 username
,autoLogin
两个属性。 然后我们写出如下代码。
function getUsername() {
return getByKey("username");
}
function setUsername(username: string) {
setByKey("username", username);
}
function getAutoLogin() {
return getByKey("autoLogin");
}
function setAutoLogin(autoLogin: boolean) {
setByKey("autoLogin", autoLogin);
}
使用的时候我们这样使用:
const username = getUsername();
setUsername("banxi");
const autoLogin = getAutoLogin();
setAutoLogin(true);
这样的话,用起来中规中矩,没毛病。 只是觉得不太 cool.
开始优雅起来
我们可以利用 getter
和 setter
让 Settings
类的使用者优雅起来。我们以其中的 username
属性为例。引入 Settings
类
class Settings {
public get username(): string {
return getByKey("username");
}
public set username(value: string) {
setByKey("username", value);
}
}
const settings = new Settings();
然后就可以向下面这样很自然的使用了。是不是看起来优雅多了?
settings.username = "banxi";
const username = settings.username;
上面的使用方式是优雅了,但是写的 getter
和 setter
看起来还是有比较多的冗余的东西。比如上面的实现中 "username" 字符串使用了两次,另外getter
和 setter
中 username
名称也出现了两次。看起来确实比较冗余。
优雅,可以更优雅一点
为了简化 Settings
类中属性的声明,我们可以引入 @decorator
,即装饰器。
增加一个名为 asSetting
的装饰器
/**
* @param defaultValue 设置项的默认值
* @param key 自定义其他的 key 值, 自定义 key 值可以避免代码 ugly 之后,属性名改变,然后
* 读取不到对应值。
*/
function asSetting(defaultValue: any, key?: string) {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDecorator
) {
const pkey = key || propertyKey;
Object.defineProperty(target, pkey, {
get: function() {
const ret = getByKey(propertyKey);
if (typeof ret === "undefined") {
return defaultValue;
} else {
return ret;
}
},
set: function(value) {
setByKey(propertyKey, value);
}
});
} as any;
}
然后我们的 Settings
类就可以改写成这样了。
class Settings {
@asSetting(false)
autoLogin: boolean;
@asSetting(null)
username: string;
@asSetting(16, "userAge")
age: number;
}
const settings = new Settings();
这样使用:
function main() {
console.log("default age expected 16 ,actual: ", settings.age);
settings.username = "banxi";
const username = settings.username;
console.info("username :", username);
settings.age = 19;
settings.autoLogin = true;
console.info("current settings:", storage);
}