优雅的 JavaScript 之 编写优雅的 Settings 类

写应用时经常需要用到保存一些用户设置的功能。怎么样在 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.

开始优雅起来

我们可以利用 gettersetterSettings 类的使用者优雅起来。我们以其中的 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;

上面的使用方式是优雅了,但是写的 gettersetter 看起来还是有比较多的冗余的东西。比如上面的实现中 “username” 字符串使用了两次,另外gettersetterusername 名称也出现了两次。看起来确实比较冗余。

优雅,可以更优雅一点

为了简化 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);
}
    原文作者:一半晴天
    原文地址: https://www.jianshu.com/p/2a4fe05ae96e
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞