如何解决Google Closure命名空间地狱?

时间:2014-01-14 05:41:34

标签: javascript google-closure-library

我的新工作是使用Google Closure库编写面向组件的JavaScript。我喜欢活动,组件,服务和模块。但由于需要编写混乱使用命名空间的代码,因此工作非常苛刻。以下代码是典型的:

goog.provide(com.bin.slash.dot.closure.widget.SuperForm);

goog.require(com.bin.slash.dot.closure.widget.Avatar);
// ... ten require calls more...

com.bin.slash.dot.closure.widget.SuperForm = function() {
  goog.base(this);
  this._internal = new com.bin.slash.dot.closure.widget.Avatar(
    com.bin.slash.dot.closure.widget.Avatar.SRC_PATH);
};

我无法相信这是真的。我并不害怕输入所有这些,但我只是觉得逻辑在这个象征地狱中消失了。扫描非常困难,因此需要更多时间来了解正在发生的事情。 我的老板说,不鼓励写下这样的快捷方式:

var SF = com.bin.slash.dot.closure.widget.SuperForm = function(){};

因为在编译之后它们都将被绑定到全局命名空间(窗口),所以它们可能会干扰别的东西。

问题是如何避免这个符号地狱?

更新:我改进了我的开发人员流程,解决了地狱的问题。 现在我编写了甜蜜的JavaScript,然后由Grunt使用sweet.js宏自动编译:

// For each file I define three macros which are replaced 
// in the compile time with hell of a long paths.
macro dir { rule { $x } => { my.very.very.long.namespace $x } }
macro class { rule { $x } => { dir.NameOfMyClass $x } }
macro proto { rule { $x } => { class.prototype $x } }

dir.NameOfMyClass = function() {}; // yields: my.very.very.long.namespaceNameOfMyClass = function() {};
class.CONSTANT = "I don't know why we write constants into classes, not prototypes"; // yields: my.very.very.long.namespaceNameOfMyClass.CONSTANT = ...;
proto.method1 = function() {}; // yields my.very.very.long.namespaceNameOfMyClass.prototype.method1 = function(){};

宏编译器创建的所有噪声都被优秀的shelljs删除。

3 个答案:

答案 0 :(得分:6)

假设您正在使用Closure Compiler,请考虑goog.scope。有内置的编译器支持,可以在优化之前替换别名变量:

在要点

  • goog.providegoog.require语法未发生变化;
  • 您可以在范围内开始声明非构造函数名称空间;
  • 构造函数名称空间需要在重命名它们的范围之外声明。

考虑到上述指南,您的代码示例可能看起来像这样:

goog.provide('my.very.long.namespace.NameOfMyClass');
goog.require('my.very.long.namespace');

/** @constructor */
my.very.long.namespace.NameOfMyClass = function() { /*...*/ };

goog.scope(function() {
  var _ = my.very.long.namespace.NameOfMyClass;

  _.CONSTANT = 'I don\'t know why we write constants into classes, not prototypes';

  _.prototype.method1 = function() {};

}); // goog.scope

附加

由于我没有足够的声誉来添加评论:

  

我的老板说,不鼓励写下这样的快捷方式:

var SF = com.bin.slash.dot.closure.widget.SuperForm = function(){};
     

因为所有这些都将绑定到全局命名空间(窗口)   编译后,他们可以干涉别的东西。

即使没有这些快捷方式,高级编译也可以将SFmyVariable等符号重命名为ga。这可能会导致与Google Analytics等外部代码发生冲突。

Closure支持的防止全局范围冲突的方法是在编译(source)之后引入一个立即调用的函数表达式。使用编译器标志:--output_wrapper "(function(){%output%})();"或符合严格模式的变体:--output_wrapper "(function(){%output%}).call(this);"。使用时,您老板不鼓励的快捷方式可以避免与外部符号发生冲突。

答案 1 :(得分:1)

如何使用立即函数创建单独的本地上下文:

(function () {
    var SF = com.bin.slash.dot.closure.widget.SuperForm = function(){};
}());

您的代码只能放在此函数中。这样你就永远不会覆盖任何全局变量。

答案 2 :(得分:1)

定义引用以简化工作流程IMO没有任何问题。

var 
widget = com.bin.slash.dot.closure.widget
widget.methA = function(){ widget.propertyA = 10}

命名空间有一个目的,虽然我不能在您的代码库上发言,但是有可能有更好的方法来组织这个库。