Javascript - 如何在多个模块中定义一个 class?

Javascript - How to define a class in multiple modules?

我有一个 class,它变得很长。我已经确定了多个不同的概念,它们一起赋予了我 class.

意义

"MyService" class 由:"auth", "storage", "数据库", ...概念。

我认为(也许我错了)在多个文件中定义 class“MyService”将是一个很好的重构。类似于:

MyService/MyService.js 从“./myService”导入我的服务;

export default function MyService() {
     if(!isInitialized) {
         myService.initializeApp(config);

         // Only after doing myService.initializeApp(config);
         this.auth = myService.auth();
         this.storage = myService.storage();
         this.database = myService.database();

         isInitialized = true;
     }
}

MyService/Auth.js

Add auth methods to MyService class prototype

MyService/Storage.js

Add storage methods to MyService class prototype

...

我该怎么做?这个技巧有name/exists吗?这是重构我的 class 的好方法吗?如果是,为什么?如果不是,那么,为什么?

谢谢。

更新

也许,将 class 拆分成更小的 classes 是最好的主意,但我不知道如何让它工作以访问 myService 功能, 这条线

myService.initializeApp(config);

之前必须在单例上执行过。

基于解决方案的示例

/* Simulate myService CLI SDK */
function myService() {}

myService.key = undefined;

myService.initializeApp = function (key) {
  this.key = key;
} 

myService.auth = function () {
  if(!this.key) {
    throw new Error("Please, initialize app");
  }

  console.log(`You have access to all auth methods! Key: ${this.key}`);
}

myService.storage = function () {
  if(!this.key) {
    throw new Error("Please, initialize app")
  }

  console.log(`You have access to all storage methods! Key: ${this.key}`);
}



/* Classes that compose the main class */

function Auth() {
  this.auth = myService.auth();
}

Auth.prototype.login = function (username, password) {
  console.log("Login!");
}

function Storage() {
  this.storage = myService.storage();
}

Storage.prototype.uploadFile = function (file, path) {
  console.log("Uploading file!")
}




/* Main class */

let isInitialized = false;
function Firebase() {
  if(!isInitialized) { // Singleton
    myService.initializeApp("raul");
    this.auth = new Auth(); // Composition
    this.storage = new Storage(); // Composition
    isInitialized = true;
  }
}

const f = new Firebase();
f.auth.login();
f.storage.uploadFile();

与其将 MyService 分散到多个模块中,不如将其分解成更小的 class。你说过:

"MyService" class is composed by: "auth", "storage", "database", ... concepts.

所以可能有一个 class 用于身份验证,另一个用于存储,另一个用于数据库,然后 MyService 使用其他 classes 的实例作为一部分它的实施。

您不能跨模块传播 class 定义。(您的编辑表明出于某种原因您没有使用 class 语法。 ) 您 可以 在 class 之后添加近方法,如下所示:

class Example {
    // ...
}

然后在另一个模块中:

import Example from "./Example.js";

// (You'd probably have a utility method for this, `addMethod` or similar)
Object.defineProperty(Example.prototype, "methodName", {
    value() {
        // ...method implementation...
    },
    enumerable: false, // (You can leave this off, false is the default)
    configurable: true,
    writable: true,
});

但是像这样分散定义可能不是最佳实践,并且存在局限性,尤其是在子classes中(super在以这种方式添加的方法中将不起作用superclass 定义中定义的方法中的方式)。 (您的编辑表明您没有使用 class 语法,因此这与您无关。)

所以我会坚持将 MyService 分解成更小的 class 可以组成的。


您可能想知道为什么我在上面使用 Object.defineProperty,而不仅仅是:

Example.prototype.methodName = function() {
    // ...
};

区别在于可枚举性。如果您使用这样的赋值添加方法,它会创建一个可枚举的 属性,但如果您使用 Object.defineProperty,则不会,如上所示。我对 enumerableconfigurablewritable 使用与 class 定义在创建方法时使用的相同标志。这是差异的示例:

class A { }
Object.defineProperty(A.prototype, "example", {
    value() {},
    enumerable: false,
    configurable: true,
    writable: true,
});

class B { }
B.prototype.example = function() { };

const a = new A();
console.log(`typeof a.example = ${typeof a.example}`);
console.log(`a's enumerable non-Symbol properties:`);
let count = 0;
for (const name in a) {
    console.log(`* ${name}`);
    ++count;
}
console.log(`Total: ${count}`);

const b = new B();
console.log(`typeof b.example = ${typeof b.example}`);
console.log(`b's enumerable non-Symbol properties:`);
count = 0;
for (const name in b) {
    console.log(`* ${name}`);
    ++count;
}
console.log(`Total: ${count}`);
.as-console-wrapper {
    max-height: 100% !important;
}

请注意 a 没有任何可枚举的属性,但 b 有——因为它继承了一个