我知道以前也曾提出过类似的问题,但是方法学变化很快,所以我试图了解当前的最佳实践。 (实际上,就在两天前,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批注,可以使用提前关闭模式将上述内容最小化,并且以下调用有效(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会引发此错误:



问:是否可以?如果可以,应如何注释上述代码才能与ADVANCED_OPTIMIZATIONS一起使用?

我知道我可以使用这种表示法:
MY["ModuleStruct"] = MY.ModuleStruct;
MY["ModuleStruct"]["prototype"]["clone"] = MY.ModuleStruct.prototype.clone;

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

最佳答案

在解决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仅适用于全局范围中定义的符号/属性”)。
  • 删除以下不需要的项目:
  • @exportthis.color上的this.size标签
  • 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

    注意:如果要运行未编译的代码,则需要加载Closure库或自己定义goog.scope():
    var goog = {};
    goog.scope = function(fn) {
        fn();
    };
    

    这是所有这些更改的a fork of your JSFiddle

    10-05 21:04
    查看更多