JavaScript 混淆压缩
原由
- JavaScript 是一种解释型脚本语言(代码不进行预编译)。是直接解析源码运行的,也就是“裸奔”的。
- 运行时需要基于源代码,所以每一个采用了 JavaScript 的设备,都能提取出 JavaScript 源码。代码安全性难以维护。
- 基于以上原因,需要考虑进行 JavaScript 源码的 “保护” 。
- 由于 JavaScript 解释型语言的特点,完全加密不太现实,可以考虑的方向是增加代码的“不可读性”。
如何增加 JavaScript 源码的不可读性
- 增加 JavaScript 代码的不可读性,主要有三个方面:压缩(Compression)、混淆(Obfuscation) 和 加密(Encryption)。
压缩(Compression)
- 这一操作的目的,是让最终代码传输量 (不代表代码量, 也不代表文件体积)尽可能小。压缩 JS 的工具,常见的有:YUI Compressor(雅虎)、Uglify、Closure Compiler(Google) 等。
- 通常在代码压缩的过程中,只改变代码的语法,代码的语义和控制流不会有太大改变。
- 常见做法是把局部变量缩短化,把一些运算进行等价替换等。代码压缩对于代码保护有一些帮助,但由于语义和控制流基本没变,起不了太大作用。
- 在压缩层面上,代码不可读只是一种附带伤害,不是最终目的。
混淆(Obfuscation)
- 这一操作的目的,是让代码尽可能地不可读,主要用作代码保护。
- 让代码不可读,增加分析的难度,这是唯一目的。混淆过后文件体积变大一倍也没关系,代码量变多也没关系,运算慢50% 也没关系。
- 常见的做法有:分离常量、打乱控制流、增加无义代码、检查运行环境如果不对就罢工,等等。
- 在混淆层面上,代码不可读是最终目的。
- 值得一提的是,Google Closure Compiler 的 Advance Level Compression 会压缩类和对象的成员,其压缩结果很难分析,也可以认为是一种混淆,但兼容性不太好。
混淆的例子:

加密(Encryption)
- 加密在 Web 界有太多歧义了。这里特指 代码加密 (将代码明文进行可逆的变换)
- 一般用eval方法加密,效果与混淆相似,也做到了压缩的效果。
- 实际上大部分 JS 加密只是对源码进行了一个字符串变换,没有深入到代码语法层面。
加密的例子:

各种工具效果对比
测试代码
'use strict';
let cal_days_labels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
let cal_months_labels = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const cal_days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
let cal_current_date = new Date();
const cal_week_start = 0; // 0:sun, 1:mon, ...
let cal_is_empty = true;
let cal_year = 2000,
cal_month = 1,
cal_day = 1;
function Calendar(year, month, day) {
cal_day = day;
cal_month = month;
cal_year = year;
}
Calendar.prototype.generateHTML = function () {
let firstDay = new Date(cal_year, cal_month, 1);
let startingDay = firstDay.getDay();
startingDay = (startingDay + 7 - cal_week_start) % 7;
let lMonthLength = cal_days_in_month[(cal_month - 1 + cal_days_in_month.length) % cal_days_in_month.length];
let monthLength = cal_days_in_month[cal_month];
if ((cal_month === 1) && ((cal_year % 4 === 0 && cal_year % 100 !== 0) || cal_year % 400 === 0)) {
monthLength = 29;
} else if ((cal_month === 2) && ((cal_year % 4 === 0 && cal_year % 100 !== 0) || cal_year % 400 === 0)) {
lMonthLength = 29;
}
let monthName = cal_months_labels[cal_month];
// document.getElementById("c-capt").innerHTML = `${monthName} ${cal_year}`;
let html = '';
for (let i = 0; i < cal_days_labels.length; i++) {
html += `<div class="dayname">${cal_days_labels[(cal_week_start + i) % 7]}</div>`;
}
document.getElementById("c-head").innerHTML = html;
html = '';
let day = 1;
let looping = true;
let bIsYearMonth = (cal_current_date.getMonth() === cal_month && cal_current_date.getFullYear() === cal_year);
for (let i = 0; i < 9 && looping; i++) {
html += '<div class="c-drow">';
for (let j = 0; j < cal_days_labels.length; j++) {
if (j < startingDay && i === 0) {
html += `<div class="day last">${(lMonthLength - startingDay + j)}`;
} else if (i > 0 || j >= startingDay) {
if (day <= monthLength) {
if(day === cal_day && bIsYearMonth) {
html += `<div class="day"><div class="current">${day}</div>`;
} else {
html += `<div class="day">${day}`;
}
} else if (day > monthLength) {
html += `<div class="day next">${(day - monthLength)}`;
looping = false;
}
++day;
}
html += '</div>';
}
html += '</div>';
}
document.getElementById("c-days").innerHTML = html;
cal_is_empty = false;
};
var cal = new Calendar(2018,12,28);
cal.generateHTML();
YUI Compressor (压缩)
"use strict";var cal_days_labels=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];var cal_months_labels=["January","February","March","April","May","June","July","August","September","October","November","December"];var cal_days_in_month=[31,28,31,30,31,30,31,31,30,31,30,31];var cal_current_date=new Date();var cal_week_start=0;var cal_is_empty=true;var cal_year=2000,cal_month=1,cal_day=1;function Calendar(year,month,day){cal_day=day;cal_month=month;cal_year=year}Calendar.prototype.generateHTML=function(){var firstDay=new Date(cal_year,cal_month,1);var startingDay=firstDay.getDay();startingDay=(startingDay+7-cal_week_start)%7;var lMonthLength=cal_days_in_month[(cal_month-1+cal_days_in_month.length)%cal_days_in_month.length];var monthLength=cal_days_in_month[cal_month];if(cal_month===1&&(cal_year%4===0&&cal_year%100!==0||cal_year%400===0)){monthLength=29}else{if(cal_month===2&&(cal_year%4===0&&cal_year%100!==0||cal_year%400===0)){lMonthLength=29}}var monthName=cal_months_labels[cal_month];var html="";for(var i=0;i<cal_days_labels.length;i++){html+='<div class="dayname">'+cal_days_labels[(cal_week_start+i)%7]+"</div>"}document.getElementById("c-head").innerHTML=html;html="";var day=1;var looping=true;var bIsYearMonth=cal_current_date.getMonth()===cal_month&&cal_current_date.getFullYear()===cal_year;for(var _i=0;_i<9&&looping;_i++){html+='<div class="c-drow">';for(var j=0;j<cal_days_labels.length;j++){if(j<startingDay&&_i===0){html+='<div class="day last">'+(lMonthLength-startingDay+j)}else{if(_i>0||j>=startingDay){if(day<=monthLength){if(day===cal_day&&bIsYearMonth){html+='<div class="day"><div class="current">'+day+"</div>"}else{html+='<div class="day">'+day}}else{if(day>monthLength){html+='<div class="day next">'+(day-monthLength);looping=false}}++day}}html+="</div>"}html+="</div>"}document.getElementById("c-days").innerHTML=html;cal_is_empty=false};var cal=new Calendar(2018,12,28);cal.generateHTML();
Google Closure Compiler (压缩)
Google Closure Compiler (压缩) - WHITESPACE_ONLY
var cal_days_labels=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];var cal_months_labels=["January","February","March","April","May","June","July","August","September","October","November","December"];var cal_days_in_month=[31,28,31,30,31,30,31,31,30,31,30,31];var cal_current_date=new Date;var cal_week_start=0;var cal_is_empty=true;var cal_year=2E3,cal_month=1,cal_day=1;function Calendar(year,month,day){cal_day=day;cal_month=month;cal_year=year}Calendar.prototype.generateHTML=function(){var firstDay=new Date(cal_year,cal_month,1);var startingDay=firstDay.getDay();startingDay=(startingDay+7-cal_week_start)%7;var lMonthLength=cal_days_in_month[(cal_month-1+cal_days_in_month.length)%cal_days_in_month.length];var monthLength=cal_days_in_month[cal_month];if(cal_month===1&&(cal_year%4===0&&cal_year%100!==0||cal_year%400===0))monthLength=29;else if(cal_month===2&&(cal_year%4===0&&cal_year%100!==0||cal_year%400===0))lMonthLength=29;var monthName=cal_months_labels[cal_month];var html="";for(var i=0;i<cal_days_labels.length;i++)html+='<div class="dayname">'+cal_days_labels[(cal_week_start+i)%7]+"</div>";document.getElementById("c-head").innerHTML=html;html="";var day=1;var looping=true;var bIsYearMonth=cal_current_date.getMonth()===cal_month&&cal_current_date.getFullYear()===cal_year;for(var i$0=0;i$0<9&&looping;i$0++){html+='<div class="c-drow">';for(var j=0;j<cal_days_labels.length;j++){if(j<startingDay&&i$0===0)html+='<div class="day last">'+(lMonthLength-startingDay+j);else if(i$0>0||j>=startingDay){if(day<=monthLength)if(day===cal_day&&bIsYearMonth)html+='<div class="day"><div class="current">'+day+"</div>";else html+='<div class="day">'+day;else if(day>monthLength){html+='<div class="day next">'+(day-monthLength);looping=false}++day}html+="</div>"}html+="</div>"}document.getElementById("c-days").innerHTML=html;cal_is_empty=false};var cal=new Calendar(2018,12,28);cal.generateHTML();
Google Closure Compiler (压缩) - SIMPLE_OPTIMIZATIONS
var cal_days_labels="Sun Mon Tue Wed Thu Fri Sat".split(" "),cal_months_labels="January February March April May June July August September October November December".split(" "),cal_days_in_month=[31,28,31,30,31,30,31,31,30,31,30,31],cal_current_date=new Date,cal_week_start=0,cal_is_empty=!0,cal_year=2E3,cal_month=1,cal_day=1;function Calendar(c,f,d){cal_day=d;cal_month=f;cal_year=c}Calendar.prototype.generateHTML=function(){var c=(new Date(cal_year,cal_month,1)).getDay();c=(c+7-cal_week_start)%7;var f=cal_days_in_month[(cal_month-1+cal_days_in_month.length)%cal_days_in_month.length],d=cal_days_in_month[cal_month];1===cal_month&&(0===cal_year%4&&0!==cal_year%100||0===cal_year%400)?d=29:2===cal_month&&(0===cal_year%4&&0!==cal_year%100||0===cal_year%400)&&(f=29);for(var a="",b=0;b<cal_days_labels.length;b++)a+='<div class="dayname">'+cal_days_labels[(cal_week_start+b)%7]+"</div>";document.getElementById("c-head").innerHTML=a;a="";b=1;for(var h=!0,k=cal_current_date.getMonth()===cal_month&&cal_current_date.getFullYear()===cal_year,g=0;9>g&&h;g++){a+='<div class="c-drow">';for(var e=0;e<cal_days_labels.length;e++){if(e<c&&0===g)a+='<div class="day last">'+(f-c+e);else if(0<g||e>=c)b<=d?a=b===cal_day&&k?a+('<div class="day"><div class="current">'+b+"</div>"):a+('<div class="day">'+b):b>d&&(a+='<div class="day next">'+(b-d),h=!1),++b;a+="</div>"}a+="</div>"}document.getElementById("c-days").innerHTML=a;cal_is_empty=!1};var cal=new Calendar(2018,12,28);cal.generateHTML();
Google Closure Compiler (压缩) - ADVANCED_OPTIMIZATIONS
var a="Sun Mon Tue Wed Thu Fri Sat".split(" "),b=[31,28,31,30,31,30,31,31,30,31,30,31],c=new Date,d=2E3,e=1,f=1;new function(){f=28;e=12;d=2018};var g=(new Date(d,e,1)).getDay();g=(g+7-0)%7;var h=b[(e-1+b.length)%b.length],k=b[e];1===e&&(0===d%4&&0!==d%100||0===d%400)?k=29:2===e&&(0===d%4&&0!==d%100||0===d%400)&&(h=29);for(var l="",m=0;m<a.length;m++)l+='<div class="dayname">'+a[(0+m)%7]+"</div>";document.getElementById("c-head").innerHTML=l;l="";for(var n=1,p=!0,q=c.getMonth()===e&&c.getFullYear()===d,r=0;9>r&&p;r++){l+='<div class="c-drow">';for(var t=0;t<a.length;t++){if(t<g&&0===r)l+='<div class="day last">'+(h-g+t);else if(0<r||t>=g)n<=k?l=n===f&&q?l+('<div class="day"><div class="current">'+n+"</div>"):l+('<div class="day">'+n):n>k&&(l+='<div class="day next">'+(n-k),p=!1),++n;l+="</div>"}l+="</div>"}document.getElementById("c-days").innerHTML=l;
Uglify (压缩)
"use strict";function Calendar(a,e,l){cal_day=l,cal_month=e,cal_year=a}var cal_days_labels=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],cal_months_labels=["January","February","March","April","May","June","July","August","September","October","November","December"],cal_days_in_month=[31,28,31,30,31,30,31,31,30,31,30,31],cal_current_date=new Date,cal_week_start=0,cal_is_empty=!0,cal_year=2e3,cal_month=1,cal_day=1;Calendar.prototype.generateHTML=function(){var a=new Date(cal_year,cal_month,1),e=a.getDay();e=(e+7-cal_week_start)%7;var l=cal_days_in_month[(cal_month-1+cal_days_in_month.length)%cal_days_in_month.length],c=cal_days_in_month[cal_month];1===cal_month&&(cal_year%4==0&&cal_year%100!=0||cal_year%400==0)?c=29:2===cal_month&&(cal_year%4==0&&cal_year%100!=0||cal_year%400==0)&&(l=29);for(var _=(cal_months_labels[cal_month],""),t=0;t<cal_days_labels.length;t++)_+='<div class="dayname">'+cal_days_labels[(cal_week_start+t)%7]+"</div>";document.getElementById("c-head").innerHTML=_,_="";for(var n=1,r=!0,d=cal_current_date.getMonth()===cal_month&&cal_current_date.getFullYear()===cal_year,s=0;s<9&&r;s++){_+='<div class="c-drow">';for(var y=0;y<cal_days_labels.length;y++)y<e&&0===s?_+='<div class="day last">'+(l-e+y):(s>0||y>=e)&&(n<=c?_+=n===cal_day&&d?'<div class="day"><div class="current">'+n+"</div>":'<div class="day">'+n:n>c&&(_+='<div class="day next">'+(n-c),r=!1),++n),_+="</div>";_+="</div>"}document.getElementById("c-days").innerHTML=_,cal_is_empty=!1};var cal=new Calendar(2018,12,28);cal.generateHTML();
JsMin (压缩)
'use strict';var cal_days_labels=['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];var cal_months_labels=['January','February','March','April','May','June','July','August','September','October','November','December'];var cal_days_in_month=[31,28,31,30,31,30,31,31,30,31,30,31];var cal_current_date=new Date();var cal_week_start=0;var cal_is_empty=true;var cal_year=2000,cal_month=1,cal_day=1;function Calendar(year,month,day){cal_day=day;cal_month=month;cal_year=year;}Calendar.prototype.generateHTML=function(){var firstDay=new Date(cal_year,cal_month,1);var startingDay=firstDay.getDay();startingDay=(startingDay+7-cal_week_start)%7;var lMonthLength=cal_days_in_month[(cal_month-1+cal_days_in_month.length)%cal_days_in_month.length];var monthLength=cal_days_in_month[cal_month];if(cal_month===1&&(cal_year%4===0&&cal_year%100!==0||cal_year%400===0)){monthLength=29;}else if(cal_month===2&&(cal_year%4===0&&cal_year%100!==0||cal_year%400===0)){lMonthLength=29;}var monthName=cal_months_labels[cal_month];var html='';for(var i=0;i<cal_days_labels.length;i++){html+='<div class="dayname">'+cal_days_labels[(cal_week_start+i)%7]+'</div>';}document.getElementById("c-head").innerHTML=html;html='';var day=1;var looping=true;var bIsYearMonth=cal_current_date.getMonth()===cal_month&&cal_current_date.getFullYear()===cal_year;for(var _i=0;_i<9&&looping;_i++){html+='<div class="c-drow">';for(var j=0;j<cal_days_labels.length;j++){if(j<startingDay&&_i===0){html+='<div class="day last">'+(lMonthLength-startingDay+j);}else if(_i>0||j>=startingDay){if(day<=monthLength){if(day===cal_day&&bIsYearMonth){html+='<div class="day"><div class="current">'+day+'</div>';}else{html+='<div class="day">'+day;}}else if(day>monthLength){html+='<div class="day next">'+(day-monthLength);looping=false;}++day;}html+='</div>';}html+='</div>';}document.getElementById("c-days").innerHTML=html;cal_is_empty=false;};var cal=new Calendar(2018,12,28);cal.generateHTML();
JSPacker (混淆加密)
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('\'1d 1c\';3 n=[\'18\',\'19\',\'1h\',\'1m\',\'1l\',\'1k\',\'1q\'];3 M=[\'1n\',\'X\',\'W\',\'V\',\'T\',\'U\',\'Y\',\'S\',\'14\',\'13\',\'11\',\'10\'];3 l=[d,D,d,p,d,p,d,d,p,d,p,d];3 r=x z();3 s=0;3 L=F;3 a=15,b=1,u=1;J v(A,O,5){u=5;b=O;a=A}v.R.E=J(){3 P=x z(a,b,1);3 g=P.Z();g=(g+7-s)%7;3 t=l[(b-1+l.m)%l.m];3 h=l[b];f(b===1&&(a%4===0&&a%B!==0||a%K===0)){h=N}o f(b===2&&(a%4===0&&a%B!==0||a%K===0)){t=N}3 1j=M[b];3 6=\'\';q(3 i=0;i<n.m;i++){6+=\'<8 e="1i">\'+n[(s+i)%7]+\'</8>\'}I.H("c-1p").y=6;6=\'\';3 5=1;3 w=F;3 Q=r.1o()===b&&r.16()===a;q(3 k=0;k<9&&w;k++){6+=\'<8 e="c-1g">\';q(3 j=0;j<n.m;j++){f(j<g&&k===0){6+=\'<8 e="5 1a">\'+(t-g+j)}o f(k>0||j>=g){f(5<=h){f(5===u&&Q){6+=\'<8 e="5"><8 e="17">\'+5+\'</8>\'}o{6+=\'<8 e="5">\'+5}}o f(5>h){6+=\'<8 e="5 1b">\'+(5-h);w=C}++5}6+=\'</8>\'}6+=\'</8>\'}I.H("c-1f").y=6;L=C};3 G=x v(1e,12,D);G.E();',62,89,'|||var||day|html||div||cal_year|cal_month||31|class|if|startingDay|monthLength|||_i|cal_days_in_month|length|cal_days_labels|else|30|for|cal_current_date|cal_week_start|lMonthLength|cal_day|Calendar|looping|new|innerHTML|Date|year|100|false|28|generateHTML|true|cal|getElementById|document|function|400|cal_is_empty|cal_months_labels|29|month|firstDay|bIsYearMonth|prototype|August|May|June|April|March|February|July|getDay|December|November||October|September|2000|getFullYear|current|Sun|Mon|last|next|strict|use|2018|days|drow|Tue|dayname|monthName|Fri|Thu|Wed|January|getMonth|head|Sat'.split('|'),0,{}))
WEB 设备的 JS 混淆压缩
- 第一优先性能,第二优先代码的私密性。
- 由此可见为了加强代码的不可读性,采用混淆压缩 JS 代码的形式不太可取。
- 综合上面的所说的方法,采用 Closure Compiler(Google) 方式对 JS 代码进行处理。
- 其中主要采用 Closure Compiler 的压缩机制(附带部分混淆的效果)。
简述 Closure Compiler(Google)
- 由 Google 公司推出。其(高级模式,Advanced)可以从语义层次对 JS 代码进行处理,完全重新改造 JS 代码,去掉不会执行到的 JS 代码。获得最大的压缩率。所以 Google 称其为 "Compiler" ,而不是 "Compressor"。
- Google Closure Compiler 支持将多个 JS 文件合并压缩为一个文件,能够通过语法分析获得更高的压缩率。
- Google Closure Compiler 提供三个运行形式:在线工具、源代码、离线工具。(详细链接请查看附录)
- Google Closure Compiler 提供三种压缩方式:Whitespace only、Simple、Advanced。
以这段代码为例:
function sayHello(name) {
alert('Hello, ' + name);
}
sayHello('Yuki');
- Whitespace only (
WHITESPACE_ONLY
):只删除空白、注释- 约束条件:不认可 JS 1.5 以上版本的语言特性。
function sayHello(name){alert("Hello, "+name)}sayHello("Yuki");
- Simple (
SIMPLE_OPTIMIZATIONS
):在WHITESPACE_ONLY
基础上将局部变量和参数转成短名称- 约束条件:完全禁用 with 和 eval。
function sayHello(a){alert("Hello, "+a)}sayHello("Yuki");
- Advanced (
ADVANCED_OPTIMIZATIONS
):更加激进的重命名、移除垃圾代码、内联函数。- 约束条件:【但是有破坏代码逻辑,运行结果的风险。需要安装规则要求修改代码,保证代码的结果】
alert("Hello, Yuki");
- 例子 1 - 原代码:
var data = {
user: "Yuki",
age: "18"
};
if ("user" in data) {
alert(data.age);
}
- 例子 1 - Advanced 压缩后:
var a={b:"Yuki",a:"18"};"user"in a&&alert(a.a);
- 例子 2 - 原代码:
var data = {
user: "Yuki",
age: "18"
};
if (data.user) {
alert(data.age);
}
- 例子 2 - Advanced 压缩后:
alert("18");
- 例子 3 - 原代码:
var data = {
"user": "Yuki",
"age": "18"
};
if ("user" in data) {
alert(data.age);
}
- 例子 3 - Advanced 压缩后:
alert("18");
使用 Google Closure Compiler 离线工具
- 基于方案特点,采用 Google Closure Compiler 离线工具进行 JS 代码的压缩。
- 需要下载 closure-compiler 的 jar 包。(建议下载最新版本)
- 在服务器运行前,需要设置 java 环境。
- 可以在终端输入 "java" ,测试是否支持 java 命令。
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_111
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
- 使用 Google Closure Compiler 离线工具 closure-compiler.jar
- 常用命令:
说明 | 命令 | 参考命令 | 备注 |
---|---|---|---|
查看帮助 | --help |
java -jar compiler.jar --help |
compiler.jar 为你的 jar 文件名称 |
设置输出文件 * | --js_output_file |
java -jar compiler.jar --js_output_file=out.js |
还需要设置输入文件 |
设置输入文件 * | --js |
java -jar compiler.jar --js_output_file=out.js --js=in1.js in2.js in3.js |
|
设置优化等级 * | --compilation_level |
java -jar compiler.jar {输入输出文件} --compilation_level=ADVANCED_OPTIMIZATIONS |
默认等级为:SIMPLE_OPTIMIZATIONS ( WHITESPACE_ONLY 、SIMPLE_OPTIMIZATIONS 、ADVANCED_OPTIMIZATIONS ) |
设置所有文件定义的编码格式 | --charset |
java -jar compiler.jar {输入输出文件} --charset=... |
|
设置报错模式 | --warning_level |
java -jar compiler.jar {输入输出文件} --warning_level=QUIET |
( QUIET 、DEFAULT 、VERBOSE ) |
设置调试选项 | --debug |
java -jar compiler.jar {输入输出文件} --debug=... |
|
设置格式化输出 | --formatting |
java -jar compiler.jar {输入输出文件} --formatting=RETTYPRINT |
( RETTYPRINT 、PRINTINPUT_DELIMITER ) |
打印语法分析树 | --print_tree |
java -jar compiler.jar {输入输出文件} --print_tree=... |
附录
- Google 官方 Closure Compiler 资料:https://developers.google.com/closure/compiler/
- Google 官方 Closure Compiler 在线工具:https://closure-compiler.appspot.com/home
- Google 官方 Closure Compiler 源码:https://github.com/google/closure-compiler
- Google 官方 Closure Compiler 离线工具:https://github.com/google/closure-compiler/wiki/Binary-Downloads
- 用 Closure Compiler 编写更好的 OO 的 JavaScript:https://www.cnblogs.com/georgewing/archive/2010/12/15/1902902.html
- JAVASCRIPT加密方法,JS加密解密综述(7种):http://www.cnblogs.com/top5/archive/2009/08/07/1540860.html
- 几种常见的JS混淆和反混淆工具 :http://www.freebuf.com/articles/web/97945.html
- 混淆工具 decent-messup:https://github.com/blackmiaool/decent-messup
- 混淆工具 jsfuck:https://github.com/aemkei/jsfuck
- S压缩、解压、格式化、混淆加密、解密(UglifyJS2、Babili、YUI compressor、JSPacker、JsMin):https://www.css-js.com/
网友评论