【问题标题】:Module pattern with Closure Compiler and ADVANCED_OPTIMIZATIONS带有闭包编译器和 ADVANCED_OPTIMIZATIONS 的模块模式
【发布时间】:2015-04-19 21:35:48
【问题描述】:

我知道以前有人问过类似的问题,但方法变化很快,所以我想了解当前的最佳做法。 (事实上​​,就在 2 天前,Chad Killingsworth 在三年前的 accepted answer 上添加了一条评论,指出 @expose 注释现在已弃用。)

我正在使用module pattern。以下代码的工作JSFIDDLE

/** @const */
var MATHCALCS = (function () {
    'use strict';

    var MY = {};

    /**
     * @constructor
     * @param {!Object} obj
     * @expose
     */
    MY.ModuleStruct = function (obj) {
        /** @expose */
        this.color = (obj.color !== undefined) ? obj.color : null;
        /** @expose */
        this.size = (obj.size !== undefined) ? obj.size : null;
    };

    /**
     * @expose
     */
    MY.ModuleStruct.prototype.clone = function () {
        return new MY.ModuleStruct({
            "color": this.color,
                "size": this.size
        });
    };


    MY.moduleProperty = 1;

    /**
     * @type {function(!Array<number>)}
     * @expose
     */
    MY.moduleMethod = function (a) {
        var i, x = 0;
        for (i = 0; i < a.length; i += 1) {
            x = x + a[i];
        }
        return x;
    };

    return MY;

}());

window["MATHCALCS"] = MATHCALCS;*

目前,使用@expose注解,上面可以用Closure提前模式缩小,下面的调用工作(minified example):

// call a public method
alert(MATHCALCS.moduleMethod([1, 2, 3]));

// allocate a new structure
var ms = new MATHCALCS.ModuleStruct({
    "color": "red",
        "size": "small"
});
alert(ms.color + '\t' + ms.size);

// clone a second instance
var ms2 = ms.clone();
alert(ms2.color + '\t' + ms2.size);
alert(ms !== ms2); // cloned objs are not equal

// and directly update the properties of the object
ms2.color = "white";
ms2.size = "large";
alert(ms2.color + '\t' + ms2.size);

如果可能,在不改变模块模式的情况下,我想更新代码(大约 10,000 行)以使用 @export 注释。但是,当我将 @expose 替换为 @export 时,Closure 会引发此错误:

错误 - @export 仅适用于在全局范围内定义的符号/属性。

问:有没有可能,如果有,上面的代码应该如何注释才能使用 ADVANCED_OPTIMIZATIONS?

我知道我可以使用这种类型的符号:

MY["ModuleStruct"] = MY.ModuleStruct;
MY["ModuleStruct"]["prototype"]["clone"] = MY.ModuleStruct.prototype.clone;

但是以这种方式导出对象属性会变得乏味。进一步的 JSLint 抱怨奇怪的分配,所以我宁愿使用 JSDocs 注释。

【问题讨论】:

  • @export 是正确的方法。看起来我们需要做一些工作来支持这种类型的用例。
  • @ChadKillingsworth,感谢您提出问题单。如果在@export 支持@expose 的这种模式之前不删除对@expose 表示法的支持,那将是非常好的。
  • @expose 支持在相当长的一段时间内不会从编译器中删除。但是,现在将发布有关其使用的警告。我将尽快改进@export

标签: javascript module google-closure-compiler


【解决方案1】:

在解决the issue raised by @ChadKillingsworth 之前,这里有一个解决方案,您只需对代码进行少量修改即可使用@export

/** @const */
var MATHCALCS = {};

goog.scope(function () {
    'use strict';

    var MY = MATHCALCS;

    /**
     * @constructor
     * @param {!Object} obj
     * @export
     */
    MY.ModuleStruct = function (obj) {
        this.color = (obj.color !== undefined) ? obj.color : null;
        this.size = (obj.size !== undefined) ? obj.size : null;
    };

    /**
     * @export
     */
    MY.ModuleStruct.prototype.clone = function () {
        return new MY.ModuleStruct({
            "color": this.color,
                "size": this.size
        });
    };


    MY.moduleProperty = 1;

    /**
     * @type {function(!Array<number>)}
     * @export
     */
    MY.moduleMethod = function (a) {
        var i, x = 0;
        for (i = 0; i < a.length; i += 1) {
            x = x + a[i];
        }
        return x;
    };

});

变化是:

  • @expose 标签更改为@export
  • 在模块包装函数之外创建一个空的MATHCALCS对象,并让MY别名指向它。
  • 不要立即执行模块包装函数 (IIFE),而是将其传递给 goog.scope()。这在作用域函数中启用了别名,允许编译器确定导出的符号是在全局 MATHCALCS 对象上定义的。这可以防止编译器引发错误(“@export 仅适用于在全局范围内定义的符号/属性”)。
  • 删除以下不需要的项目:
    • this.colorthis.size 上的 @export 标签
    • return MY;
    • window["MATHCALCS"] = MATHCALCS;

使用此命令编译时:

java -jar compiler.jar \
    --js closure/goog/base.js \
    --js mathcalcs.js \
    --js_output_file mathcalcs.min.js \
    --compilation_level ADVANCED_OPTIMIZATIONS \
    --generate_exports \
    --formatting PRETTY_PRINT \
    --output_wrapper '(function() {%output%}).call(window);'

你会得到:

(function() {var f = this;
function g(a, d) {
  var b = a.split("."), c = f;
  b[0] in c || !c.execScript || c.execScript("var " + b[0]);
  for (var e;b.length && (e = b.shift());) {
    b.length || void 0 === d ? c[e] ? c = c[e] : c = c[e] = {} : c[e] = d;
  }
}
;function h(a) {
  this.color = void 0 !== a.color ? a.color : null;
  this.size = void 0 !== a.size ? a.size : null;
}
g("MATHCALCS.ModuleStruct", h);
h.prototype.clone = function() {
  return new h({color:this.color, size:this.size});
};
h.prototype.clone = h.prototype.clone;
g("MATHCALCS.moduleMethod", function(a) {
  var d, b = 0;
  for (d = 0;d < a.length;d += 1) {
    b += a[d];
  }
  return b;
});
}).call(window);

g() 函数是 goog.exportSymbol() 的编译版本 - 有关详细信息,请参阅 the @export docs

注意:如果你想运行未编译的代码,你需要加载闭包库,或者自己定义goog.scope()

var goog = {};
goog.scope = function(fn) {
    fn();
};

这里是a fork of your JSFiddle,包含所有这些更改。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    • 1970-01-01
    • 2020-01-23
    相关资源
    最近更新 更多