Javascript压缩原理及压缩工具收集
一、为什么要进行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搜索相关的更多内容
原文:http://www.cnbruce.com/blog/showlog.asp?log_id=1389
by simpman