2

在本教程中提到的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

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

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

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

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

  • 未编译一个关于关闭编译伟大的事情是,你可以运行所有的代码未编译。该过程中的一个必要步骤是使用depswriter.py,这是一个可读取所有源文件(查找goog.providegoog.require调用)并生成文件deps.js的Python程序。 deps.js文件是命名空间依赖关系树的实例。这里是一个样本线(333)从我的项目的deps.js文件:

    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); 
    

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

  • 编译编译器(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指出的那样,使用闭包编译器“非常冗长” - 它需要使用注释来标注JavaScript代码,以告诉每个方法(函数)的输入和输出类型以及对象的每个属性的类型(类)。我认为按照你的建议要求编译器“理解”你的代码,并猜测你认为什么是类,以及你认为是构造函数和什么是构造函数。方法或该类的其他属性。这会比闭包编译器设计者所遇到的问题困难得多 - 特别是因为JavaScript是一种“松散”语言,可以让你几乎可以做任何你能想到的事情。

实际上我发现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的对JavaScript的关闭编译器是去除死皮/无用的代码,同时最小化和模糊处理来运行你的谷歌代码闭幕你使用的作品。

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

+0

谁说我不使用'goog.provide'?请仔细阅读问题。 – CodeYogi

+0

我在开头添加了一段。也许我还没有理解你的问题? – owler

+0

更新我的问题,希望现在清楚。 – CodeYogi

1

有两件事情:

  1. 只能唤起goog.provide()每个命名空间一次。

您目前可能有你在一个文件中定义的“阶级”,说Note.js,与goog.provide('tutorial.notepad');现在。但是,如果添加另一个文件,如Tab.js,其中包含“类”tutorial.notepad.Tab,则当Tab.js也称为goog.provide('tutorial.nodepad')时,您将碰到this error

  • 调用goog.provide('tutorial.notepad')没有告诉关闭编译器对“类” tutorial.notepad.Note
  • 谷歌封闭码是在它的原始库形式非常冗长。使用它的真正原因是通过javascript-to-javascript Closure编译器运行您的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。