本日志标题:Javascript压缩原理及压缩工具收集        [2011-11-24]

一、为什么要进行Javascript压缩?

1、减少JS代码容量,增加下载速度和执行速度;

2、压缩后的JS代码不具备可识性,在一定程度上达到加密效果,防止被人轻易使用。

二、常规Javascript压缩的原理:

1、压缩多余的空格和换行符;

2、删除注释,因为注释对浏览用户是无用的,删除了可以缩小文件体积;

3、把较长的变量名称和过程名称统一替换为很短的名称。

三、其他Javascript压缩(加密)原理
http://www.cnblogs.com/heimirror/archive/2010/06/04/1751304.html

1、加密:微软有一个控件,可以将javascript加密,加密后真的是一点都无法阅读,在运行的时候需要再用这个控件解密。加解密的过程是可逆的,所以有些基础的人还是可以破解的。此外,这个控件在浏览器里运行时会报安全警告,而且只适用于IE,所以用处不大。

2、加壳:有些工具可以将js文件打包成exe,在运行时类似于以cscript/wscript运行,这样的方法是在网页里行不通的。

3、去冗:将javascript中的注释、换行、没用的空字符都去掉,这样的做法没什么技术含量,如果没注释,用编辑器就可以做。

4、重整:利用javascript的eval函数以及正则表达式将源文件重整,这个过程也是可逆的,重整后的文件只能对付菜鸟,而且会影响加载效率。

5、压缩:这个是重点了,大家看一下jQuery的min包,主体部分除了经过去冗处理,函数内部的变量也被替换了,这样可以在不考虑阅读的情况下,最大程度减少文件长度。这个过程也是很有技术含量的,因为不能修改全局变量,否则压缩后的文件可能无法使用。具体实现方法在网上似乎传了很多,其实目前只有一个,就是yuicompressor,我特意在jQuery的SVN上看了一下,在build目录里可以找到这个包,目前的版本是2.4.2。

四、特别介绍YUI Compressor压缩JavaScript原理
http://www.planabc.net/2009/08/02/javascript-compression_with_yui_compressor/

YUI Compressor 压缩 JavaScript 的内容包括:
1、移除注释

2、移除额外的空格

3、细微优化

4、标识符替换(Identifier Replacement)

YUI Compressor 包括哪些细微优化呢?

object["property"] ,如果属性名是合法的 JavaScript 标识符(注:合法的 JavaScript 标识符——由一个字母开头,其后选择性地加上一个或者多个字母、数字或下划线)且不是保留字,将优化为: object.property

{"property":123} ,如果属性名是合法的 JavaScript 标识符且不是保留字,将优化为 {property:123} (注:在对象字面量中,如果属性名是一个合法的 JavaScript 标识符且不是保留字,并不强制要求用引号引住属性名)。

'abcd\'efgh',将优化为 "abcd'efgh"。

"abcd" + "efgh",如果是字符串相连接,将优化成 "abcdefgh"(注:所有在使用 YUI Compressor 的前提下,对于脚本中的字符串连接,使用连接符 “+” 的效率和可维护性最高)。
对于 JavaScript 最有效的压缩优化,当属标识符替换。

比如:


(function(){
function add(num1, num2) {
return num1 + num2;
}
})();


进行属标识符替换后:


(function(){
function A(C, B) {
return C+ B;
}
})();


再移除额外的空格,最终成了:


(function(){function A(C,B){return C+B;}})();



YUI Compressor 标识符替换仅替换函数名和变量名,那哪些不能被替代呢?

1、原始值:字符串、布尔值、数字、null 和 undefined。一般来说字符串占的空间最多,而非数字字面量其次(true、false,null,underfinded)。

2、全局变量:window、document、XMLHttpRequest等等。使用最多的就是 document、window。

3、属性名,比如:foo.bar。占据的空间仅次于字符串,”.” 操作符无法被代替,且 a.b.c 更加费空间。

4、关键字。经常被过度使用的关键字有:var、return。最好的优化方法:一个函数仅出现一次 var 和 return 关键字。

对于原始值、全局变量、属性名的优化处理方式大致相同:任何字面量值、全局变量或者属性名被使用超过 2 次(包括2次),都应该用局部变量存储代替。

但有部分情况下是禁止使用标识符替换的:

1、使用 eval() 函数。解决方法:不使用或者创建一个全局函数封装 eval()。

2、使用 with 语句。解决方法:方法同上。

3、JScript 的条件注释。唯一解决的方法:不使用。

由于 YUI Compressor 是建立在 rhino interpreter 基础上的,所以上述所有的优化都是安全的。


++++++++++++++++++++++++++++++++++

扩展阅读:避免两种比较邪恶的写法:
http://younglab.blog.51cto.com/416652/302712

1.eval()
eval的作用是把字符串当成JavaScript语句来执行,我们经常用它来处理ajax请求后返回的值。

但是eval有很多副作用,比如能访问全局变量:


var message = "Hello world!";

function doSomething() {
eval("alert(message)");//Hello world!
}


上面的代码,如果使用YUI Compressor的变量替换原则来压缩的话就是:


var A = "Hello world!";

function doSomething() {
eval("alert(message)");
}


因为eval()中的是字符串,所以YUI Compressor不会对其进行变量替换,这样一来压缩后的JavaScript就会出现错误。

而YUI Compressor为了保证JavaScript的正确性,在遇到eval的时候,就不会压缩eval相关的变量。

eval用得比较多的地方就是处理Ajax回调后的数据,例如:


function handleJSONP(object) {
return object;
}

function interpretJSONP(code) {
var data = eval(code);

//process data
}


使用eval来转换回调后得到的字符串。但是使用eval带来了全局变量被覆盖的危险,所以最好的方法是减少使用全局变量。

有时候,你可能会把一些函数放在一个自动执行的匿名函数内,例如:


(function() {
function handleJSONP(object) {
return object;
}

function interpretJSONP(code) {
var data = eval(code);

//process data
}
})();


而由于其中出现了eval,所以整个匿名函数都不会进行变量替换,
它会被压缩成:


(function(){function handleJSONP(object){return object}function »
interpretJSONP(code){var data=eval(code)}})();


可以看到,代码段中的变量没有进行任何的压缩替换。
更好的写法是:


function myEval(code) {
return eval(code);
}

function handleJSONP(object) {
return object;
}

(function() {
function interpretJSONP(code) {
var data = myEval(code);

//process data
}
})();


压缩后变成了:


function myEval(code){return eval(code)}function handleJSONP »
(a){return a}(function(){function a(b){var c=myEval(b)}})();



2.with
使用with,可以避免重复地写同一个变量名。例如:


var object = {
message: "Hello, ",
messageSuffix: ", and welcome."
};
object.message += "world" + object.messageSuffix;
alert(object.message);


可以写成:


var object = {
message: "Hello, ",
messageSuffix: ", and welcome."
};
with (object) {
message += "world" + messageSuffix;
alert(message);
}


另外,如果object中没有某个属性是,with中使用到的变量会沿着作用域链去寻找它的定义,例如:


function displayMessage(object) {
var message = "Yo, ";

with (object){
message += "world" + messageSuffix;
alert(message);
}
}

displayMessage({ message: "Hello, ", messageSuffix: ", and welcome." });
displayMessage({ messageSuffix: ", and welcome." });



第二次调用时,输出的会是“Yo, world, and welcome.”

不像eval,with没有什么更好的写法,而YUI Compressor也不会对with所在的函数进行变量替换,上面的函数压缩后是:


function displayMessage(object){var message="Yo, ";with(object) »
{message+="world"+messageSuffix;alert(message)}};


大小是112 bytes。
如果不使用with,


function displayMessage(object) {
var message = "Yo, ";

object.message += "world" + object.messageSuffix;
alert(object.message);
}


压缩后是:


function displayMessage(a){var b="Yo, ";a.message+="world"+ »
a.messageSuffix;alert(a.message)};


大小是93 bytes,可以看到比使用with减少了不少。


在百度搜索完整的Javascript压缩原理及压缩工具收集内容,或者用Google搜索相关的更多内容

By [cnbruce] at 17:46:51 | 评论 [4] | 浏览 [22634] | TrackBack| 返回顶部

About Comments
学习了!谢谢
By [jysns] at 2012-5-6 11:56:22
高手就是高手,我的网站请各位大侠指教。
http://www.361steel.com
By [361steel] at 2012-11-11 16:54:23


Post a Comment
呢称: 验证码: 
禁止笑脸转换 禁止UBB | 缩放输入框:6 5 | [Ctrl+Enter提交](1000个字符限制)