【问题标题】:Namespace issue in google closure library谷歌闭包库中的命名空间问题
【发布时间】:2016-08-11 13:37:18
【问题描述】:

教程中提到here,模块提供的命名空间是:

goog.provide('tutorial.notepad.Note');

但我想知道为什么不这样:

goog.provide('tutorial.notepad');

因为,根据下面提到的规则:

tutorial = tutorial || {};
tutorial.notepad = tutorial.notepad || {};
tutorial.notepad.Note = tutorial.notepad.Note || {};

如果我们只是提供:

goog.provide('tutorial.notepad'); 那么,我们已经有了:

tutorial = tutorial || {};
tutorial.notepad = tutorial.notepad || {};

我们可以添加属性Note

tutorial.notepad.Note = function() {};

因此,我的问题是:

为什么不直接声明goog.provide('tutorial.notepad'),然后使用它来包含顶级Classes,而是建议为每个Class 使用goog.provide('tutorial.notepad.Note'),这对我来说是多余的。

【问题讨论】:

    标签: javascript google-closure-compiler google-closure-library javascript-namespaces


    【解决方案1】:

    拥有goog.provide('tutorial.notepad'); 会在该命名空间的“依赖树”中创建一个条目,但不会为tutorial.notepad.Note 类创建一个条目。如果您在示例代码中手动创建 tutorial.notepad.Note,那么您不会激活闭包编译器机制以将类 tutorial.notepad.Note 包含到闭包编译器使用的命名空间依赖关系树中。

    原因是goog.provide 被闭包编译器用来建立依赖树,用于确定要加载的命名空间和顺序。

    通过不使用goog.provide,而是用您显示的代码模仿其效果,编译器不会了解Note 类以及它如何适应命名空间和类及其依赖关系的树。

    有两种方法可以运行基于闭包编译器的代码:已编译和未编译。它们中的每一个都以不同的方式构建和使用命名空间依赖树:

    • 未编译 闭包编译器的一大优点是您可以运行所有未编译的代码。在这个过程中一个必要的步骤是使用depswriter.py,这是一个 Python 程序,它读取你所有的源文件(寻找goog.providegoog.require 调用)并生成一个文件deps.jsdeps.js 文件是命名空间依赖树的体现。这是我项目的deps.js 文件中的一个示例行(共 333 行):

      goog.addDependency('../../../src/lab/app/ViewPanner.js',
        ['myphysicslab.lab.app.ViewPanner'], ['myphysicslab.lab.util.DoubleRect',
         'myphysicslab.lab.util.UtilityCore', 'myphysicslab.lab.util.Vector',
         'myphysicslab.lab.view.CoordMap', 'myphysicslab.lab.view.LabView'], false);
      

    当我在未编译状态下运行我的代码时,有一个 <script> 标记运行该 deps.js 脚本。这样做会导致创建命名空间依赖树的内存版本,goog.require 在运行时访问它以加载该特定类所需的任何其他文件。

    • COMPILED 编译器(Java 程序)在编译过程中执行与上述相同的操作。不同之处在于,生成的命名空间依赖树仅在编译期间用于确定定义类的顺序,确定需要什么等。编译完成后,命名空间依赖树将被丢弃。

    参考资料:

    https://github.com/google/closure-compiler/wiki/Managing-Dependencies

    https://github.com/google/closure-compiler/wiki/Debugging-Uncompiled-Source-Code


    回复您的评论:

    为什么不直接声明goog.provide('tutorial.notepad'),然后使用它来包含顶级Classes,而是建议为每个Class 使用goog.provide('tutorial.notepad.Note'),这对我来说是多余的。

    我认为这涉及到闭包编译器的目标和设计问题。正如@Technetium 指出的那样,使用闭包编译器“非常冗长” - 它需要用 cmets 注释您的 JavaScript 代码,以告知每个方法(函数)的输入和输出类型以及对象(类)的每个属性的类型)。

    (我不是编译器专家,但是)我认为按照您的建议进行操作需要编译器“理解”您的代码并猜测您认为什么是类,以及您认为什么是构造函数和方法或该类的其他属性。这将是一个比闭包编译器设计者解决的问题困难 - 特别是因为 JavaScript 是一种如此“松散”的语言,它允许你做几乎任何你能想到的事情。

    在实践中,我发现goog.provide 一点也不麻烦。我通常只为每个文件定义一个类。我发现更麻烦的是所有goog.require 语句。我通常可以在一个文件中包含 20 或 30 个这些文件,并且这个文件列表经常在类似的类中重复。我的代码中有 3870 次 goog.require

    即使这样也可以,但更糟糕的是闭包编译器有一个goog.scope 机制,可以让你使用更短的名称,就像我可以说Vector 而不是new myphysicslab.lab.util.Vector。这很好,但问题是你已经 goog.required 的每个类你都必须在 goog.scope 中创建一个短变量,如下所示:

    var Vector = myphysicslab.lab.util.Vector;
    

    无论如何,我的观点是:是的,闭包编译器需要比原始 JavaScript 更多的代码。但goog.provide 是这方面最少的问题。

    还有一件事:用户@Technetium 声明

    使用它的真正原因是通过 javascript-to-javascript Closure Compiler 运行您的 Google Closure 代码,该编译器可以删除死/未使用的代码,同时最小化和混淆您使用的部分。

    虽然这是一个非常有用的功能,但使用闭包编译器还有另一个非常重要的原因:类型检查。如果您花时间将注释添加到您的函数中,那么编译器将通过捕获错误来“支持您”。这对任何项目都有很大帮助,但当您有多个开发人员在一个项目上工作时变得至关重要,这也是 Google 开发闭包编译器的主要原因之一。

    【讨论】:

    • 谁说我不用goog.provide?请仔细阅读问题。
    • 我在开头添加了一个段落。也许我还没有理解你的问题?
    • 我添加了对您更新的问题的回复,希望它有意义。
    • 类型检查的要点。它在接口上特别有用。
    • 那么,我可以推断的基本上是“您可以轻松地在另一个文件/包中要求该类”,对吗?
    【解决方案2】:

    这里有几件事:

    1. 每个命名空间只能调用一次goog.provide()

    您目前可能将您的“类”定义在一个文件中,例如Note.js,现在使用goog.provide('tutorial.notepad');。但是,如果您添加另一个文件,例如 Tab.js,其中包含“类”tutorial.notepad.Tab,那么当 Tab.js 也调用 goog.provide('tutorial.nodepad') 时,您将遇到 this error

    1. 调用goog.provide('tutorial.notepad') 不会告诉闭包编译器关于“类”tutorial.notepad.Note

    Google Closure 代码的原始库形式极其冗长。使用它的真正原因是通过 javascript-to-javascript Closure Compiler 运行您的 Google Closure 代码,该编译器可以删除死/未使用的代码,同时最小化和混淆您使用的部分。虽然您的示例在调试模式下工作,因为它没有利用 Closure Compiler,但一旦运行 Closure Compiler 并尝试构建依赖关系映射,当某些东西试图通过 goog.requires('tutorial.notepad.Note') 引用它时,它将无法找到 tutorial.notepad.Note 类。如果您想了解有关此依赖关系图如何工作的更多信息,owler 的回答是一个很好的起点。

    顺便说一句,请注意我在引号中使用“class”,而且是有意的。虽然 Google Closure 通过其 @constructor 注释以及通过 goog.provide/goog.require 语法粗略模拟 package/import 在许多方面提供了面向对象编程的外观和感觉,但归根结底它仍然是 JavaScript。

    【讨论】:

      猜你喜欢
      • 2015-05-20
      • 2012-04-09
      • 1970-01-01
      • 1970-01-01
      • 2017-05-12
      • 1970-01-01
      • 1970-01-01
      • 2014-03-09
      • 2011-02-05
      相关资源
      最近更新 更多