问题
I use uglifyjs to minify a concatenated set of files, which works fine but not good enough. The built lib uses namespaces, so classes, functions and constants are stored in a root namespace variable:
(function() {
var root = { api:{}, core:{}, names:{} };
/* util.js file */
root.names.SOME_LONG_NAMED_CONST='Angel';
/* Person.js file */
root.core.Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root.api.perform;
})();
which is minified to the not-so-minimal version
(function(){var a={api:{},core:{},names:{}};a.names.SOME_LONG_NAMED_CONST="Angel",a.core.Person=function(a){this.name=a},a.api.perform=function(){},window.lib_name.perform=a.api.perform})();
I understand uglify probably thinks that root var is a data structure that must be kept as-is and can't be changed. Is there a way to let uglify mangle the nested names in the root namespace?
回答1:
When you minimize Javascript you can only change names of variables, the api, core and names are not variables but properties of an object. If these were changed by the minimizer, you would potentially get unexpected results. What if in your code you would call
root["api"].perform = function()...
or even something like
function doIt(section, method, argument) {
root[section][method](argument);
}
doIt('api','perform', 101);
All perfectly legal JS, but a minimizer could never figure out what's going on.
回答2:
I have been trying to use --mangle-props
of UglifyJS2 and can tell you: 'it makes a mess'.
As someone pointed out: 'Developer should decide what properties to mangle, not uglifyjs'
I am approaching the problem using this options:
--mangle-props
--mangle-regexp="/_$/"
The regex matches any property with a underscore at the end.
You asked to mangle nested names in the root namespace. So, your code:
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST_='Angel';
root.core.Person_ = function(name) { this.name = name };
root.api.perform_ = function(param_for_api) { }
window.lib_name.perform = root.api.perform;
})();
Would result in this:
(function() {
var n = {
api: {},
core: {},
names: {}
};
n.names.a = "Angel";
n.core.b = function(n) {
this.name = n;
};
n.api.c = function(n) {};
window.lib_name.perform = n.api.c;
})();
Command:
uglifyjs --beautify --mangle --mangle-props --mangle-regex="/_$/" -- file.js
If you want to mangle first level of root namespace (api, core, names)
just put a underscore on them (api_, core_, names_)
, you are in control ;)
Just a side note: when you are mangling properties usable by other js files, you should mangle all files together with the same command, so the same identifier will be used over all files.
回答3:
Aside from @JanMisker 's point (which is completely valid), rewriting properties is unsafe because they can be exposed to code outside the scope of the minification.
Although the self executing function has a scope, and if the code is only
(function() {
var root = { api:{}, core:{}, names:{} };
root.names.SOME_LONG_NAMED_CONST='Angel';
alert(root.names.SOME_LONG_NAMED_CONST); // some code that does something
})();
It is true that outside of the function, there is no way to access the root object, so rewriting the property names is safe, and the following code would result in the same:
(function() {
var a = { b:{}, c:{}, d:{} };
a.d.e='Angel';
alert(a.d.e);
})();
But even if you are inside your private scope you can access, and more importantly assign to variables from an outer scope! Imagine this:
(function() {
var root = { api:{}, core:{}, names:{} };
root.api.perform = function(param_for_api) { /* do something */ }
window.lib_name = root.api;
})();
You are not only exposing a function but an object with a function on it. And the function will be visible from any place where window is visible.
So, for example writing the following in the javascript console would yield different results with and without minification:
window.lib_name.perform(asdf);
With minification you would have to write:
window.lib_name.f(asdf);
Or something similar.
Remember that there can always be code outside your minification.
It is not that crucial to have the absolute minimal JS, but if IT IS that crucial for some reason (for example: aliens abducted your stepdaughter, and the only way to have her back is to minify this below 100 characters or so), you can manually replace an undesirably long property name to a shorter one, just be sure that it will not be exposed anywhere, and isn't be accessed through associative array notation (root['api']
).
回答4:
as @Jan-Misker explained in his answer, property name mangling is NOT an good idea because it could potentially break your code.
However, you can workaround it by define the property names as local variables, and modify all .properties to [keys], to make smaller file size:
(function() {
var API = 'api';
var CORE = 'core';
var NAMES = 'names';
var SLNC = 'SOME_LONG_NAMED_CONST';
var root = {};
root[API]={};
root[CORE]={};
root[NAMES]={};
/* util.js file */
root[NAMES][SLNC] ='Angel';
/* Person.js file */
root[CORE].Person = function(name) { this.name = name };
/* API.js with the functions we want to expose */
root[API].perform = function(param_for_api) { /* do something */ }
window.lib_name.perform = root[API].perform;
})();
Because now all the properties became a local variable, uglify js will mangle/shorten the variable names and as consequence you overall file size reduced:
!function(){var a="api",b="core",c="names",d="SOME_LONG_NAMED_CONST",e={};e[a]={},e[b]={},e[c]={},e[c][d]="Angel",e[b].Person=function(a){this.name=a},e[a].perform=function(){},window.lib_name.perform=e[a].perform}();
However, reduced file size doesn't mean you will get shorter downloading time on real server, because usually our http transport is gzipped, most of the repetitions will be compressed by your http server and it does a better job than human.
回答5:
The latest release of uglify (today) has object property mangling, see v2.4.18. It also supports reserved files for excluding both object properties and variables that you don't want mangled. Check it out.
Use the --mangle-props
option and --reserved-file filename1.json filename2.json
etc....
来源:https://stackoverflow.com/questions/16334971/mangle-nested-classes-and-variables-with-uglifyjs