在提到的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
,这对我来说是多余的。
答案 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.provide
和goog.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
在运行时访问它以加载该特定类所需的任何其他文件。
参考文献:
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)
有几件事情在这里发挥作用:
goog.provide()
次。 您目前可能拥有&#34;类&#34;现在在单个文件中定义,Note.js
,goog.provide('tutorial.notepad');
。但是,如果你添加另一个文件,比如Tab.js
,那就是&#34;类&#34;其中tutorial.notepad.Tab
,当Tab.js
同时调用goog.provide('tutorial.nodepad')
时,您将遇到this error。
goog.provide('tutorial.notepad')
并不会告诉Closure Compiler关于&#34;类&#34; tutorial.notepad.Note
强> 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的结尾。那一天。