【问题标题】:Nested namespaces vs. ES modules嵌套命名空间与 ES 模块
【发布时间】:2020-07-22 20:18:22
【问题描述】:

使用传统的 JavaScript 命名空间技术,我能够创建嵌套的逻辑分组,甚至可以跨越多个文件。我应该如何用 ES 模块别名导入替换它。

考虑我希望我的所有代码都在 CompanyName 命名空间中的情况,然后按产品分组,在一个产品中我可能有一组共享函数,这些函数可以按主题分组(在 Java、C# 等许多语言中很常见ETC。)。 F.e.

CompanyName = {
    ProductName1: {
        Shared: {
            Database: {
                enumerateDatabases: function() {
                    ....
                }
                ...
            }
        }
    },
    ProductName2: {
        ....
    }
}

如果我理解正确,ES 模块允许我只使用一个别名来创建命名空间,f.e.

import * as CompanyName from ...

仅像 CompanyName 这样的别名肯定不够具体。像

这样的别名
import * as CompanyNameProductName1SharedDatabase from...

几乎不可读,是一个简单的别名,例如

import * as Database from...

可能很快就会变得模棱两可,我将不得不使用像

这样的别名
import * as MyDatabase from...
import * as YourDatabase from...

这是否意味着无法如上所示对代码进行分组?还是我应该同时使用这两种技术?

【问题讨论】:

  • 只是好奇:在使用嵌套命名空间时,您是如何引用 enumerateDatabases 函数的?
  • 在这种情况下看起来确实很难看:'let dbs = CompanyName.ProductName1.Shared.Database.enumerateDatabases()'。当然,我可以尽可能使用本地缩写,即使是命名空间的一部分。关键是,完整的命名空间在需要时可用!

标签: javascript ecmascript-6 es6-modules


【解决方案1】:

如果我理解正确,ES 模块允许我只使用一个别名来创建命名空间

是的,别名必须是简单的标识符,没有嵌套。请注意,CompanynameProductname1SharedDatabaseCompanyname.Productname1.Shared.Database 相比并没有那么糟糕——即使在其他语言中,您也不会每次都写完整的包名,而是给它一个合理的别名。甚至可以使用CPSDb 之类的缩写。

import * as Database from... 这样的简单别名可能很快就会变得模棱两可

不,不应该。 Product2 不太可能需要导入 Product1 的数据库。只有在同一个模块中确实有多个数据库时,您才需要消除歧义。

只需为 current 模块选择合理的别名。这可能意味着根据您导入它的位置使用不同的别名,这很好:

// src/productname1/shared/index.js
import * as Database from './database.js';
// src/backend/init.js
import * as Productname1SharedDatabase from '/productname1/shared/database.js';

另外请注意,在 JS 中使用命名空间导入是相当罕见的,它们仅在您从一个模块导入许多绑定时使用。并且需要从其他模块中消除它们的歧义。你会看到

import { enumerateDatabase } from './database.js';

更多的是,很明显enumerateDatabase 函数会在数据库模块中找到。请注意,JavaScript 中没有隐式绑定:如果您忘记了标识符的来源,您将始终在当前文件中找到它的(导入)声明,以便您轻松查找。此外,您的 IDE 将通过显示名称解析到的位置来帮助您。

【讨论】:

  • 谢谢,很多宝贵的意见。让我有点沮丧的是,我认为经典命名空间是一个高度系统化的概念。当您自己编写时,使用 ES 模块我必须适应手头的情况。一个应用程序中的多个数据库几乎是我的日常事务,因为我必须将数据从 A 转换和移动到 B 等。因此,避免冲突并知道什么属于哪里对我来说非常重要。我认为我的主要批评者是:在 C# 和 Java 或经典 JS 命名空间中,您有一个防水概念来消除歧义和分组事物。使用 ES 模块更像是我必须检查哪个别名有效......
  • @NicolasR 当然,您可以提出自己的约定/方案,以了解如何从文件路径构建别名(因为这些是标识模块的标识,类似于 java 包)。系统分组应该用文件夹来完成。
  • @NicolasR 当然,使用嵌套对象作为命名空间的概念并没有死。您仍然可以让模块导出具有其公共子命名空间属性的对象并使用它,仅使用模块在单独的文件中创建每个命名空间的成员。 (但是,当模块导入“父级”时,您可能不得不更多地担心循环依赖)
  • 好的,我接受你的回答。也许最后一个问题:你知道我是否可以为多个模块使用相同的别名,比如 f.e. 'import * as CompanyName from "file-system-utils"' 和'import * as CompanyName from "database-utils"'。然后我可以将 CompanyName 作为顶级命名空间并在模块中使用对象命名空间。只是出于好奇...
  • 不,别名不能在一个模块的范围内发生冲突,就像其他变量声明一样。你宁愿做import * as CompanyName from '..';,并让根目录导出 filesystemutils 和一个 databaseutils 常量(每个都从各自的模块导入)。
【解决方案2】:

你会这样做的方式是

const enumerateDatabases = function() {
        ....
    }
export {
    enumerateDatabases
}

import {enumerateDatabases as aliasForEnumDbs} from 'xx.js';

【讨论】:

  • 是的,这样的例子随处可见,但是:你如何处理别人有一个函数'enumerateDatabases'的情况?当您将其称为“let dbs = CompanyName.ProductName.Shared.Database.enumerateDatabases()”时,可以轻松识别函数的来源,这不是一个很好的功能。好的,这个例子很极端,但我希望你明白这一点。
  • 感谢您的更新。这当然有效,但我正在寻找一个适用于具有许多潜在冲突的大型代码库的概念。给单个函数起别名似乎相当费力。想象一个模块中有 100 个实用程序函数和 50 个模块......我接受了 Bergis 的回答,因为他提供了更多的背景和替代方案。
猜你喜欢
  • 2013-09-06
  • 1970-01-01
  • 2011-07-06
  • 2015-02-17
  • 1970-01-01
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
  • 2010-11-12
相关资源
最近更新 更多