谷歌关闭库中的命名空间问题

时间:2016-04-19 11:39:41

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

在提到的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,而是建议每个goog.provide('tutorial.notepad.Note')使用Class,这对我来说是多余的。

2 个答案:

答案 0 :(得分:3)

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

原因是闭包编译器使用goog.provide来设置依赖关系树,用于确定要加载的命名空间以及加载顺序。

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

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

  • UNCOMPILED 关闭编译器的一个好处就是您可以运行所有未编译的代码。该过程中的一个必要步骤是使用depswriter.py,这是一个Python程序,它读取所有源文件(查找goog.providegoog.require调用)并生成文件deps.js。该deps.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,而是建议每个goog.provide('tutorial.notepad.Note')使用Class,这对我来说是多余的。

我认为这涉及到关闭编译器的目标和设计问题。正如@Technetium指出的那样,使用closure-compiler&#34;非常冗长&#34; - 它需要用注释来注释你的JavaScript代码,以告诉每个方法(函数)的输入和输出类型以及对象(类)的每个属性的类型。

(我没有编译专家但是)我认为按照你的建议进行操作需要编译器理解&#34;你的代码,并猜测你认为是什么类,以及你认为是该类的构造函数和方法或其他属性。这将是一个很多更难的问题,而不是封闭编译器设计者所遇到的问题 - 特别是因为JavaScript是这样一个&#34;松散的&#34;这种语言可以让你做几乎你能想到的任何事情。

在实践中,我发现goog.provide并不麻烦。我通常每个文件只定义一个类。我发现更多的麻烦是所有goog.require陈述。我通常可以在一个文件中有20或30个这样的文件,这个文件列表经常在类似的类中重复。我的代码中有3870次出现goog.require

即使这样也没问题,但更糟糕的是,封闭编译器有一个goog.scope机制,允许你使用较短的名称,就像我可以说Vector而不是new myphysicslab.lab.util.Vector }。这很好,但问题是你已经goog.require的每个班级都需要在goog.scope内用这样的一行创建一个短变量:

var Vector = myphysicslab.lab.util.Vector;

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

还有一件事:用户@Technetium陈述

  

使用它的真正原因是通过javascript-to-javascript Closure Compiler运行你的Google Closure代码,它可以删除死/未使用的代码,同时最大限度地减少和混淆你使用的部分。

虽然这是一个非常有用的功能,但使用闭包编译器还有另一个非常重要的原因:类型检查。如果你花时间将注释添加到你的函数中,那么编译器将会#34;让你的后退&#34;通过捕捉错误。这对任何项目都是一个很大的帮助,但是当你有多个开发人员从事项目工作时这变得至关重要,这也是Google开发闭包编译器的主要原因之一。

答案 1 :(得分:1)

有几件事情在这里发挥作用:

  1. 每个命名空间只能唤起goog.provide()次。
  2. 您目前可能拥有&#34;类&#34;现在在单个文件中定义,Note.jsgoog.provide('tutorial.notepad');。但是,如果你添加另一个文件,比如Tab.js,那就是&#34;类&#34;其中tutorial.notepad.Tab,当Tab.js同时调用goog.provide('tutorial.nodepad')时,您将遇到this error

    1. 调用goog.provide('tutorial.notepad')并不会告诉Closure Compiler关于&#34;类&#34; tutorial.notepad.Note
    2. Google Closure代码在其原始库表单中非常冗长。使用它的真正原因是通过javascript-to-javascript Closure编译器运行您的Google Closure代码,该编译器删除死/未使用的代码,同时最小化和混淆您 使用的部分。虽然您的示例在调试模式下工作,因为它不利用Closure Compiler,一旦运行Closure Compiler并尝试构建依赖关系图,当某些东西试图通过{{引用它时,它将无法找到tutorial.notepad.Note类。 1}}。如果你想进一步了解这个依赖图是如何工作的,那么owler的答案是一个非常好的起点。

      顺便说一句,请注意我使用&#34; class&#34;在引号中,并且非常有意。虽然Google Closure通过goog.requires('tutorial.notepad.Note')注释以及@constructor通过package/import语法的粗略模拟,以多种方式呈现面向对象编程的外观和感觉,但它仍然是JavaScript的结尾。那一天。