- 浏览: 209285 次
- 性别:
- 来自: 北京
最新评论
-
Relucent:
likaiyihou51 写道The constructor ...
获得Hibernate Criteria实际SQL的方法 -
likaiyihou51:
The constructor CriteriaJoinWal ...
获得Hibernate Criteria实际SQL的方法 -
likaiyihou51:
大侠 我copy了这个代码 又个问题,能给看一下吗
获得Hibernate Criteria实际SQL的方法 -
haisee:
管用,Firefox和Chrom验证通过。
Javascript设置和获取Textarea的光标位置的方法 -
wanghaosvse:
请问楼主,有没有跟数据库同步的动态加载
带选择框的JS树控件2 (为JSTree再次提速)
【转】Closure Compiler 高级模式及更多思考
前言
Google Closure Compiler 是 Google Closure Tools 的一员,在 2009 年底被 Google
释出,早先,有 玉伯 的 Closure Compiler vs. YUICompressor,主要就 压缩率
上进行了对比,另外有 承玉 的 应用 closure compiler 高级模式,对 CC
的高级模式做了些介绍
本文将详细介绍 CC 的高级模式部分,更重要的是,阐述 CC 高级模式背后的思考
CC 是真正的编译器
Closure Compiler 和 YUICompressor 并不是同类产品,虽然 CC 和 YC 同样产出压缩后的 JS 文件,
但是 YC 只做了词法上的扫描,而 CC 并不只是一个 compressor 那么简单,器如其名,它是一个
compiler
对于一个 compiler,一般地,它需要做到
- 检查源文本中语法、语义、语用上的错误
- 根据分析产出物(符号表、语法树等)产出目标 / 中间代码
- 优化
代码错误一般来自三个方面:
- 语法(Syntax)
表示构成语言句子的各个记号之间的组合规律
大体上,parser / interpreter 在词法分析和语法分析阶段,产生符号表、语法树等分析产出物,
具体见编译原理教科书……语法上的错误,如:
doSomething(;) // SyntaxError: Unexpected token ;
根据语法规则,在非
for
语句中的;
意义是分隔符,而分隔符前的(
并没有配对)
,
因此报错 - 语义(Semantics)
表示各个记号的特定含义(各个记号和记号所表示的对象之间的关系)
compiler 需要根据语义分析产出中间代码,对于不产生中间代码的语言如 JS,则在运行时的
解释期间指出错误语义上的错误,如:
0 = {}; // ReferenceError: Invalid left-hand side in assignment
根据赋值运算符
=
的意义,左操作数不能为字面量,所以虽然这个赋值语句包含了必需的
左操作数、运算符、右操作数,仍然出错 - 语用(Pragmatics)
表示在各个记号所出现的行为中,它们的来源、使用和影响
语用上的错误,如:
doSomething(); // ReferenceError: doSomething is not defined
在这里直接调用了一个未定义的函数,导致出错
在一些其他场景中,虽然程序运行正确无误,但是仍然可以优化(这种优化并不是技巧上的),比如:
function doSomethingElse() {}
(function() {
return;
doSomethingElse(); // No Exception but Redundant: Unreachable code
})();
在这里,doSomethingElse 函数之前由于有 return,因此这个函数调用将永远不能执行,这种冗余代码
对整个程序来说毫无用处,可以去掉
对于 Closure Compiler 来说,它处理的对象是 js,不需要产生其他中间代码或汇编代码 / 机器码,
因此输出的还是 js,但是是经过分析的、优化后的 js;另外,它也可以选择输出 parse tree(使用
–print_tree 参数),所以,CC 的确完成了一个编译器需要实现的功能
CC 功能概述
在详细讨论 CC 的高级模式前,还是简明介绍一下功能体系
编译级别
CC 的 compilation_level 包括三个级别:
- WHITESPACE_ONLY
只删除空白、注释
- SIMPLE_OPTIMIZATIONS
在 WHITESPACE_ONLY 基础上将局部变量和参数转成短名称
- ADVANCED_OPTIMIZATIONS
更加激进的重命名、移除垃圾代码、内联函数
可以看到,SIMPLE_OPTIMIZATIONS 级别的 CC,和 YC 无异,没做什么真正的编译工作,所以说,
使用了高级模式的 CC 才是四肢健全的 CC =。=
约束条件
使用 CC 有一定约束条件,这影响到我们的编码风格:
- WHITESPACE_ONLY
- 不认可 JS 1.5 以上版本的语言特性
- 不保留注释
- SIMPLE_OPTIMIZATIONS
- 完全禁用
with
和eval
- 字符串中引用的函数名 / 参数名不会改动(CC 不改动所有字符串)
- 完全禁用
- ADVANCED_OPTIMIZATIONS 模式下的约束放到下文详述
注解
Annotations 也是 CC 的重要组成部分,使用 JSDoc 风格,用以辅助高级模式下的编译,下文详述
使用 CC 高级模式
在 CC 下,启用高级模式的方法是加入参数 --compilation_level ADVANCED_OPTIMIZATION
作为一个 compiler,CC 的高级模式下,额外的优化政策是
- 更激进的重命名,如 obj.property 改为 a.b,将深度过高的命名空间平坦化等
- 移除垃圾代码,如删除未被调用的方法定义,警告逻辑死角(return 后的语句等)
- 将函数内联,如 a call b, b call c,a(),那么直接执行 c()
要达到高级模式的预期优化效果,开发者必须对自己做一些约束,因为 js 是弱类型、动态性的。否则
js 的这种灵活将使 compiler 无能为力
总体上,这种约束包括限定某些 js 编码风格,以及使用相应的 JSDoc 注解
以下详述具体的约束以及代码的检查 / 优化效果:
强类型的模拟
- @param 和 @type 中定义的类型会在编译期间得到检查,同样避免了在运行时检查,提高性能
- @const 标记常量,当常量被写时会报错
- 模拟枚举,将同类可枚举常量定义为一个对象字面量,使用 @enum 标记:
var STATUS = { LOADING: 3, COMPLETE: 4 };
编译结果中
STATUS.LOADING
会被直接替换为 3,其实完全模拟了 C 等语言中的枚举 - 使用 @constructor 标注函数为构造器,它仅能被实例化,而不可用作普通方法,
甚至是工厂方法,CC 会确保构造器被合法使用,否则报错。这样确保开发者不必在运行时
判断,构造器函数到底以怎样的形式被调用 - 在表达式中也可以使用 @type 来限定类型,这对于 JSON 特别有用,如
var data = /** @type {UserModel} */({ firstName : 'foo', lastName : 'bar' });
在这里 UserModel 是个构造器,也可以使用 @typedef 来自定义复杂的数据类型
域可见性的模拟
- 使用 @private 标注私有域,私有域被外部引用会报错。开发者也可以按照“国际惯例”给私有域加上
_
前缀或后缀,以提醒自己 / 协作者这是一个私有域,@private 注解用来告诉 CC;
这样,开发者可以不必使用诸如老道的“模块模式”等技巧来真正地隐藏私有变量,将检查工作丢给
CC,让开发尽可能朴实简单 - 类似有 @protect
类系统的模拟
- 使用 @extends 标注继承关系,继承体系会被优化
- 使用 @interface 标注接口,接口是类似
function ThisIsAInterface(obj) {}
的函数体为空的构造器定义,编译后将移除其相关代码。同时,标注 @implements 的构造器必须实现
implemented 的接口的所有方法(正如其他 OO 语言一样),否则,CC 报错。这同样简化了接口 /
实现的约束,靠 CC 来保证实现关系的可靠性
条件编译的模拟
- 使用 @define 标记状态开关,适用于调试 logger 等 开发 / 发布 状态需要分离的模式。
可以在编译时指定参数来标识 define 参数的状态。这其实就是一个条件编译,真给力……
对象平坦化及属性名缩减
- 对象属性会被编译为单变量,比如
foo.bar to foo$bar
,这种标记方法看起来很像 java
中被编译出来的内部类~~之后foo$bar
被进一步缩短。对象之所以能被平坦化是因为在
js 中对象可以看做是一群引用 / 原始数据类型的容器 - 但是,js 对象实际上更复杂,所以被平坦化后会带来一些副作用,比如如果在对象(字面量)中使用
this 指针,则编译后的结果会导致 this 指向错误。所以 Google 建议仅在 constructor 和
prototype methods 中使用 this,这意味着,在所谓类单例(对象字面量)和类的静态方法(绑定到
constructor 上的函数)中都避免使用 this 指针 - 在缩减对象属性 / 方法的名称长度时,有另外一个注意点,那就是必须始终使用 dot syntax(
.
运算符),而不使用 quoted string([]
运算符),除非索引名是一个变量。这是因为 CC
始终不处理字符串中的内容,所以,var o = { longName: 0 }; o["longName"]
会被翻译为var a = { b: 0 }; a["longName"]
导致出错。实在想使用 quoted string,则在 定义的时候
也要使用 quoted string - 对于全局变量,如果出现以 window.property 的形式引用的,必须始终定义为 window.porperty 形式:
window.property = 1; var property = 1; // wrong!
否则也会杯具,CC 可不会 window.property 翻译为 window.a
垃圾代码的移除
- 一个函数声明却未被调用时,默认地,声明体将被干掉
- 在这种机制下,如果一个方法是以 for in 的形式调用的,那么原方法也会被干掉,因为这种 动态特征
使得 CC 无法清楚方法是否确实在 for in 的时候被调用了 - 对于一些 unreachable 的代码,CC 将报警告
- 如果要产出一份被调用的公共接口,例如库,使用称作 export 的方法将函数导出,防止函数定义被
CC 回收。具体的做法是将函数绑定到某个容器,比如:function displayNoteTitle(note) { alert(note['myTitle']); } // Store the function in a global property referenced by a string: window['displayNoteTitle'] = displayNoteTitle;
对于需要 export 的函数,均使用 quoted string 风格
背后的思考
根据以上高级模式优化的行为分析可知,CC 附加给开发者的约束主要有:
- 强制以强类型的静态语言风格编写 js,将关注点从 运行时的动态技巧 转移到 组织代码、编写逻辑
本身。而可能由弱类型系统和动态特征产生的问题和风险则交给 CC,即通过开发者与 CC 达成一种
编码约定 而规避掉 - 严格要求区分 面向开发者的代码 和 面向机器的代码。
虽然不像 C 等语言会编译产生目标代码,但是 CC 在一定程度上也生成了 面向机器的 js,
包括压缩空白、缩减标识符、条件编译和冗余代码去除。这和第一点其实是一脉相承的,同样要求开发者 将关注点转移到开发本身
- 使用规范化的接口方式。
这不仅包括要求开发者使用恰当的 annotation(extend, interface, …),同时也给整个 OO-JS
打下了一个框架,开发者必须使用同样的模式进行 OO 编码另外,要求使用 export 技术统一导出公共接口更强化了这一点
总之,这一点进一步限定了开发者的编码风格,但是带来的好处是明显的:可读、可控、一致性
曾经有读过 Closure Library 源码的同学评论道:
Google 根本不懂怎么写 javascript!代码里面各种冗余,并且充满了 java 的味道!
当时确实也有这种感觉,比如 Google 把 if(foo)
写作 if(foo != undefined)
等等
Javascript 固然充满了丰富的动态特征,而且很多特性非常优雅,能够让代码简洁精悍,或者构造出一些
令人惊叹 的技巧,但是也会产生一些副作用:
- 首要的问题是可读性,静态的东西容易一目了然,动态的东西需要经过一番运算才能得出结论。
比如 js 中的 极晚绑定,再比如 标识符运行时重写 - 其次的问题是执行性能。一个比较经典的众 js 工程师都在使用的技巧就是“模拟函数重载”——
在函数体内判断 arguments 的特征,从而对应给出不同的逻辑。由于缺乏强类型,js 本身不能具备
真正的重载,但是运行时的判断在带来灵活性的同时,必然会多出很多模拟重载的逻辑,降低性能
在今年的 D2 大会上,Hedger 同学指出,大多数 js 开发者像是个 ninja(忍者),他们身怀绝技、
神鬼莫测,单兵作战还可以,但是一旦碰到 army(军队,比如 Google 团队这样的 =,=)就是个悲剧
我比较欣赏这个比喻,大团队要良好地协作,必需遵循一定的规范和限制,优先保证可读性和一致性,与此同时
失去的是奇技淫巧、自由灵活。所以采用何种编程风格、理念,需要具体问题具体分析…………
至少,目前 CC 提供了一个好的思路,它的高级模式推崇的编程风格也是很值得尝试、借鉴的
最后附上 CC 的常用命令选项……选项实在是有够多……
CC 常用命令选项
- –charset VAL 对所有文件定义的编码格式
- –compilationlevel [WHITESPACEONLY | SIMPLEOPTIMIZATIONS | ADVANCEDOPTIMIZATIONS]
设定编译级别 - –debug 开启 debug 选项
- –define (–D, -D) VAL 设定文件中使用 @define 标注的开关值,即条件编译
- –externs VAL 编译代码需要调用未编译的代码时,使用它
- –formatting [PRETTYPRINT | PRINTINPUT_DELIMITER] 格式化输出
- –js VAL 输入文件,多指定多个,将会被合并
- –jsoutputfile VAL 输出文件,如果不指定的话,直接输出到 standard output 流
- –module VAL 定义模块
- –output_manifest VAL 打印编译文件清单
- –print_tree 打印语法分析树
- –warning_level [QUIET | DEFAULT | VERBOSE] 设定报错模式
原文链接:http://ued.taobao.com/blog/2010/12/23/advanced-optimization-in-closure-compiler-and-more/
发表评论
-
Base64编码解码(JavaScript版本)
2010-12-17 10:24 2849<html> <HTML> < ... -
JavaScript 中2个等号与3个等号的区别
2010-12-07 13:21 25379首先,== equality 等同,=== identity ... -
JS前端获取上传文件的大小的方法
2010-11-01 11:04 23531WEB应用中,经常会遇到上传附件的问题,比如有时候会有限制附件 ... -
Javascript设置和获取Textarea的光标位置的方法
2010-07-20 11:05 6637<html><head> <m ... -
ExtJS 3.1.0 中Ext.data.Store关于baseParams的BUG修正
2010-07-15 09:59 4998ExtJs 中,我们使用GirdPanel的时候往往需要给后 ... -
JavaScript JSON 对象深拷贝方法(一例)
2010-07-14 20:08 6505其实深拷贝一个JS对象方法十分简单,只需要两部 1.首先将J ... -
关于Extjs异步session超时问题处理
2010-07-08 17:46 4049关于Extjs异步session超时问题处理对于HTTP超时的 ... -
ExtJS 的组建扩展方法
2010-07-08 17:19 2265ExtJS 是一个不错的JS 类库,提供了很多组建。 ... -
JS 地图移动拖拽
2010-05-26 09:06 2808很简单的东东,就是类似google地图那样,可以拖拽图片。 ... -
JS移动的方块
2010-05-21 09:00 1410PS:WASD上下左右,双击加速. 同时按下WASD任意2个按 ... -
JS对象序列化为JSON对象
2010-04-17 10:07 10719/** * JSON 解析类 * Copyright ... -
JavaScript日期格式化
2010-03-17 11:17 1584DateFormat = (function(){ ... -
Ext智能提示 - Spket(Eclipse插件)
2009-10-23 13:01 2008Spket是Eclipse的一个插件,RIA的开发工具。支持J ... -
带选择框的JS树控件2 (为JSTree再次提速)
2009-10-14 12:00 9576以前写过一个带选择框的JS树控件 但是当时发现一个问题,第一次 ... -
带选择框的JS树控件
2009-10-12 11:09 2113前阵子自己写了个带选择框的tree控件,虽然还有一些bug没有 ... -
多附件上传
2008-10-29 23:48 1509uploads.js var UploadBuilder=(f ... -
HTML通过button触发input-file控件上传文件的问题
2008-10-20 10:32 25653出于安全方面的考虑,通过JS修改input-file的valu ... -
模拟线程Timer(JS)
2008-09-22 09:06 1897<script language="Java ... -
转换Unicode (JS)
2008-09-02 17:34 2642<html> <head> ... -
JS-Cookie
2008-08-05 11:33 1359/** * cookie管理对象 */ Cook ...
相关推荐
**使用Google Closure Compiler进行JavaScript压缩** Google Closure Compiler是一款强大的JavaScript优化工具,由Google开发,它能够对JavaScript代码进行压缩、优化和格式化,以提高网页加载速度和减少网络带宽...
在Web开发中,常见的JavaScript压缩工具有很多,如UglifyJS、Terser、Closure Compiler等。这些工具能够删除不必要的空白、注释,甚至对变量名进行混淆,以达到最大限度地减小文件大小的目的。它们通常有命令行接口...
- **UglifyJS**:一款强大的JavaScript压缩器,支持ES6语法,提供了更多自定义选项。 - **Google Closure Compiler**:谷歌提供的压缩工具,可以处理复杂的JavaScript代码,提供高级优化功能。 - **Terser**:源自...
Closure Compiler的JS压缩比YUI Compressor更为强悍,这是因为Closure Compiler能理解JavaScript的类型系统,可以进行更深层次的优化。它有三个压缩级别:WHITESPACE_ONLY(仅去除空白)、SIMPLE_OPTIMIZATIONS...
谷歌Closure Compiler是一款强大的JavaScript代码压缩工具,它能够对JS代码进行优化,提升网页加载速度,减少文件大小,从而提高用户体验。Closure Compiler通过删除未使用的变量、函数,以及优化代码结构,将...
4. **Closure Compiler**: Google提供的JavaScript压缩工具,提供简单和高级两种压缩模式。 5. **YUI Compressor**: Yahoo开发的JS和CSS压缩工具,对代码进行压缩和优化。 四、自动化工作流 在实际开发中,开发者...
标题中提到的"js压缩工具"是一种专门针对JavaScript文件进行优化的工具,它可以将单个或多个JavaScript文件合并并压缩,消除其中的空白、注释和不必要的字符,同时进行代码混淆,使文件更紧凑,加载更快。这个工具...
- Google Closure Compiler:谷歌提供的一个高级JS压缩工具,它不仅压缩代码,还能进行类型检查和优化。 - YUI Compressor:雅虎开发的工具,可以压缩JavaScript和CSS,具有较高的压缩率。 - Minify:一个PHP库,...
Closure Compiler是一款由Google开发的JavaScript代码优化工具,它能够压缩、优化和混淆JavaScript代码,从而提高代码执行效率并减小文件大小,这对于Web应用的性能优化至关重要。 【描述】提到的"capture-replay-...
常用的JavaScript压缩工具有UglifyJS、Terser和Google Closure Compiler等。这些工具不仅能压缩代码,还能进行死代码检测和消除,进一步优化文件大小。 二、CSS(css)压缩 CSS压缩与JavaScript压缩类似,也是去除...
为了提高网站的加载速度和性能,对这三种文件进行...`jscompress_package_vc.exe`作为这个工具的一部分,可能是专门处理JavaScript压缩的关键组件。对于任何前端开发者来说,掌握这样的工具和优化技巧都是至关重要的。
Closure Compiler Maven 插件是开发JavaScript应用程序的一个强大工具,它集成在Maven构建流程中,利用Google的Closure Compiler对JavaScript代码进行优化和压缩。Closure Compiler以其先进的语法分析和优化技术著称...
标题中的"压缩CSS/JS等文件的工具"指的是一类能够对样式表(CSS)和脚本(JS)文件进行压缩的软件或在线服务,它们的主要目标是减少文件大小,从而缩短网页的加载时间。 压缩CSS文件主要是通过删除不必要的空格、换...
这个工具最初是作为Google内部项目的一部分开发的,但后来开源,成为了开发者们广泛使用的JavaScript压缩工具。 Closure Compiler有两种主要的运行模式:简单模式(Simple)和高级模式(Advanced)。简单模式主要...
"JsCssZip.exe"看起来是一个专门用于JS和CSS文件压缩的工具,可能包含有JS和CSS的压缩功能,并且可能还支持将多个文件打包成ZIP格式,方便管理和分发。使用这样的工具,开发者可以一键完成文件压缩,简化工作流程,...
"压缩web程序中的js文件"这个主题,主要是讲解如何对Web应用中的JavaScript文件进行压缩,以减小文件大小,加快页面加载速度。这一过程通常分为以下几个步骤: 1. **代码混淆**:通过改变变量名和函数名,使代码变...
3. **死代码删除(Dead Code Elimination)**:一些高级的压缩工具如Google的Closure Compiler,能分析代码并删除未使用的变量、函数等,以达到更深度的压缩。 4. **树摇优化(Tree Shaking)**:这是ES6引入的一种...
在JS压缩中,Ant脚本可以调用诸如YUI Compressor、UglifyJS或Closure Compiler等第三方压缩工具,对JS和CSS文件进行处理。在Eclipse中,可以通过配置Ant任务来自动化这个过程,只需一键执行,就能完成整个项目的压缩...
不过,随着时间的推移,市场上出现了更多功能强大且更新频繁的JavaScript压缩工具,如Google的Closure Compiler、UglifyJS以及YUI Compressor等。这些工具通常提供了更高级的优化选项和更好的社区支持,但如果你对JS...
UglifyJS 不像Closure Compiler那样具有高级的类型检查和优化功能,但它支持更多的ES6特性,适合处理现代JavaScript代码。 批量压缩JS工具通常包含以下功能: 1. **批处理能力**:可以一次性处理多个JS文件,节省...