带有 Closure Compiler 和 ADVANCED_OPTIMIZATIONS 的模块模式
Module pattern with Closure Compiler and ADVANCED_OPTIMIZATIONS
我知道以前有人问过类似的问题,但方法变化很快,所以我想了解当前的最佳做法。 (事实上,就在 2 天前,Chad Killingsworth 在三年前的 accepted answer 中添加了一条评论,即 @expose
注释现已弃用。)
我正在使用以下代码的 module pattern. Working 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
注释。但是,当我用 @export
替换 @expose
时,关闭会引发此错误:
ERROR - @export only applies to symbols/properties defined in the global scope.
问: 是否可以,如果可以,上面的代码应该如何注释才能与 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
仅适用于在全局范围内定义的 symbols/properties”)。
- 删除以下不需要的项目:
this.color
和 this.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()
– see the @export
docs 的编译版本以获得更多详细信息。
注意:如果你想运行代码未编译,你需要加载Closure Library,或者自己定义goog.scope()
:
var goog = {};
goog.scope = function(fn) {
fn();
};
这是包含所有这些更改的 a fork of your JSFiddle。
我知道以前有人问过类似的问题,但方法变化很快,所以我想了解当前的最佳做法。 (事实上,就在 2 天前,Chad Killingsworth 在三年前的 accepted answer 中添加了一条评论,即 @expose
注释现已弃用。)
我正在使用以下代码的 module pattern. Working 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
注释。但是,当我用 @export
替换 @expose
时,关闭会引发此错误:
ERROR - @export only applies to symbols/properties defined in the global scope.
问: 是否可以,如果可以,上面的代码应该如何注释才能与 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
仅适用于在全局范围内定义的 symbols/properties”)。 - 删除以下不需要的项目:
this.color
和this.size
上的 return MY;
window["MATHCALCS"] = MATHCALCS;
@export
标签
使用此命令编译时:
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()
– see the @export
docs 的编译版本以获得更多详细信息。
注意:如果你想运行代码未编译,你需要加载Closure Library,或者自己定义goog.scope()
:
var goog = {};
goog.scope = function(fn) {
fn();
};
这是包含所有这些更改的 a fork of your JSFiddle。