google closure library

来源:互联网 发布:如何设置电子狗数据 编辑:程序博客网 时间:2024/04/29 19:08

 自从Google释出了其Closure的JavaScript库以来,越来越多人希望了解它与Ext JS比起来究竟怎么样。由于我也属于这些想知道的人,所以我希望从我自己的角度来回答此问题,希望并不会由此触动双方的感情。

参考文档http://closure-library.googlecode.com/svn/docs/index.html

Hello World

切入某个库的话,通常都是从"Hello World"程序开始。不太清楚这是不是一个没有用的例子,还是它想告诉你这个库有多么不错。反正我觉得Google Closure的Hello World例子没啥吸引俺的。Hello World例子中是调用一个JavaScript函数来创建一个DOM对象,演示了一个goog.dom.createDom的helper方法。它是通过body标签的load属性调用的,有点难看了嘛。个人认为Ext的Hello World还是蛮不错。它用Ext.onReady的标准方式调用,使得在等待图片和其他资源之前就可以初始化JavaScipt代码。并采用了Ext.Window器件来告诉您Ext器件是如何配置的和启动的,让您了解更多。

Widget部件的基类

UI组件在Ext的对象层次中,以Tree为例子就是: 


Ext.util.Observable
 Ext.Component
   Ext.BoxComponent
     Ext.Container
       Ext.Panel
         (Ext.tree.TreePanel)


UI组件在Google Closure的对象层次中

goog.Disposable
 goog.events.EventTarget
   goog.ui.Component
     (goog.ui.tree.BaseNode)

类goog.events.EventTarget应该是等价于Ext JS的Ext.util.Observable类, goog.Disposable 和goog.ui.Component应该是等价于Ext JS的Ext.Component类。而等价于Ext.BoxComponent, Ext.Container和Ext.Panel的类,在 Google Closure的对象层次中就没有找到,这些都是负责处理组件的布局问题的。

布局管理器Layout manager

Ext Js吸引我众多原因之中的一个原因,就是当初YahooUI!没有提供一个强大的布局管理器,而Ext Js是有的,——我便选用了Ext JS。
Google Closure好像就没有布局管理器了,这点我接受不了,因为没有了这玩意我不好干活!Google的意思是这些活儿让你自主发挥,所以你可以继续使用CSS或HTML的表格去布局,然后摆上你Widget部件就可以了。

事件处理Event handling

Google Closure的事件处理方面的逻辑代码编排在events.EventTarget类中,而对应 Ext Js的就放在Ext.util.Observable 类中。我发现,Google的方案好像只能在一个事件名称中指定一个处理函数。Ext的好像更为强大一些,额外地您可以指定函数作用域,延时,单体,缓冲和目标的参数。还有一点Ext让我更讨好的,那就是addListener方法中可用“on”等价之,也就是缩写了。Google Closure侦听事件的名称还是用addEventListener。用 dispatchEvent来触发事件,即等于Ext的fireEvent方法。

API文档

不得不说Ext的文档从感官和可用性来做得比其他开源软件要来的好,所说的开源软件自然也包括了Google Closure的documentation。
Google Closure的文档浏览起来有点怪,例如右下角是放置类的引用,搞得你要不断地滚动着它。而Ext的文档划分为两个面板的设计就似乎更轻松。其实每个类其文档的页面像Ext的那样就好。话说回来,由于我还没有使用Google Closure开发的经验,所以文档的帮助性有多大还是未知。嗯,没错Ext的文档中心还是我当初使用Ext的那样的好。

项目历史与开发者社区

我不知道Google Closure内部用了多久,应该很久吧(以Gmail 05启动开始算)。Ext 大概06年在研发,到现在应该也为数不少人开发人群了吧,有数字透露是500,000 developers (参见这个presentation)。你不能估量Google的边际去到哪,但反观Apple's iPhone vs. Google Android,小弟的结论是没有足够的理由说服自己从一个阵营立刻跳到对方阵营。

压缩混淆器

除了Google Closure库之外,Google还释出了Closure Tools,其中就有一个compiler作为压缩和混淆JavaScript文件的工具,一并发布。如果应用在ExtJS身上,和我们一贯使用的YUICompressor又有什么区别呢,Ext论坛上的一张帖子或许会告诉我们答案。
第一轮测试过后,论坛大大"Condor"指出,compiler压缩出来的ext-all-debug.js要比YUICompressor的文件体积小20KB。不清楚YUICompressor的大小是多少,也不知道他是不是想说压缩得很少。对于我来说这是没啥区别的。尽管后来他也说了如果压缩的时候加入更多项目代码进去的话,压缩率就会更理想的说。大家有兴趣的话请关注这张帖子。

值得一提的是,Firebug的扩展Closure Inspector还可以调试已混淆的JavaScript代码。

 

Ext GWT vs. Ext JS vs. Google Web Toolkit vs. Google Closure

Google是这样宣传Google Web Toolkit的:“让Java软件开发者更轻松地写出如Google Maps和Gmail的AJAX应用程序”(Java software development framework that makes writing AJAX applications like Google Maps and Gmail easy for developers ...),

现在我就有点奇怪了,宣传Closur的时候,Google说这个库是Google旗下所有诸如Gmail的AJAX程序的底层。我想知道,Google Web Toolkit和Google Closurede关系是不是等同于 Ext GWT与Ext JS的关系,就是GWT运行以其他的应用(Java)来写JS库里面的功能。

另外,在放到浏览器之前,compile你的代码就可以做一次代码检查,也会报告错误,GWT就没有这功能了吧,因为Google Colsure现在是纯Js compile了。(翻译不顺,能者帮忙改)

License

不想多说,跟许多人一样,译者认为协议是Ext的明显诟病之一。所以以下内容不翻译了,————翻了就太枪手所为。
Ext JS is released under a dual license, which let's you use it in a GPL-based open-source application for free or in a closed commercial application for $329 per developer or less with volume discount.
Google Closure is freely available under the Apache License. jQuery creator John Resig apparently would have preferred the MIT license, since he just tweeted "I wish that Google Closure (the lib) was under the MIT license, can't really borrow code from it for jQuery, otherwise."
There are developers that avoid Ext JS because of the costs and license, others may choose Ext JS especially because of it and its commercial support.
The preference and choice is up to the developers and companies.
If you aren't a license expert yet, check out this comparison between the different type of licenses.

Grid widget

ExtJS居于我心目中的首位,很大程度跟这个Grid有关。我在项目中想要的就是仿MS Excel的Grid,我看到Ext JS的 grid,立刻被秒杀了。遗憾地是,Google Closure好像不打算提供这类东东,我唯一能找到可以控制表格数据的就是这个goog.ui.TableSorter类。Grid,,,没有就没有吧,反正AJAX又不是说只准一个库在这儿待着,我找第三方的就是了。

Tree widget

Tree器件往往是人们使用JavaScript库的开始,他们需要一个Tree的控件引入到他们的项目中。不同于Grid的待遇,在Closure里可以找到Tree部件(例子),Ext的例子在这里。太多部件的比较超出了本文的范围,我说的重点是,,,Google Closure里面是有Tree部件的。

 

范例或演示

Ext被广泛使用的一点,而且又是让人津津乐道的是,它的例子很完整!您完全可以靠这些丰富例子,就地取材很快的建立起一个像样的程序来。Google Closure也提供一批例子,在这儿(点击右边tab的“Demo”)。随着项目的不断研发,应该会有更多的例子加入。

使用率

毫无疑问,Google Closure的使用率明显强于Ext,君不见那么多的Gmail,Google Doc和Google Maps都在用着。同时也不时很对知名的公众网站在使用Ext。首要的原因是Ext更多地是用在内联网的应用程序。Ext绝对是致人印象深刻的客户端,您不妨看看这里。据我所知,使用Ext的实现有这三款的最好,Marketo,Zipwhip和Kohive。三款程序都很好地发挥了Ext的功用。

Default design

Ext JS的默认设计和模板比Google Closure的好很多,是不是?一个问题我经常看见设计师和Mac的用户会不用Ext默认的设计,因为ExtJs的默认设计是模仿的MS Windows的。你可以看看下面的例子,当然也允许你自己修改这些界面的。Ext 3的CSS模块划分更清晰而且有可视的CSS文件帮助您。

 

单元测试Unit Testing

对于单元测试的技术态度,Google明显强于Ext的态度,这点可以从Closure的命名空间和包管理方面可以看出来,为单元测试做好准备。
当然UI库是否要求单元测试必备尚有争议,但我觉得还是必须有若干的测试的。不过虽然Closure为单元测试创造了条件,但未知其真实测试会是怎么样。

http://blog.csdn.net/zhangxin09/article/details/4828070



文件结构图

说明: 下面我们先看看Google Closure的源文件物理结构图,有个比较直观的了解,后面会具体进一步分析,具体的类,命名空间和物理结构的关系

图例:

分析:

  • goog 是顶级文件夹,它又包含了更多的子文件夹(如:timer、ui)
  • 每个子文件夹下面一般都包含了具体的JS源文件(如:tooltip.js)
  • 有时候子文件夹下面还包含了更下一级的文件夹(如ui下面的editor),而更下一级文件夹又包含了JS源文件
  • 这里大部分深度是两层文件夹,第三层是JS源文件,而最大的深度是三层文件夹,第四层是JS源文件
  • 总的来说,整个组织结构还是比较扁平简单的,没有太复杂的组织规划

一个比较完整的类实现实例(toolTip.js)

说明:这里是摘 自Google Cloure Lib中的一个ui类 Tooltip的实现,中间为了简化,把一些重复功能的语句给删减掉了,不影响我们下面对类实现细节的分析

代码:

  1. //Tooltip 类
  2. goog.provide('goog.ui.Tooltip');
  3. goog.provide('goog.ui.Tooltip.State');
  4.  
  5. goog.require('goog.Timer');
  6. goog.require('goog.array');
  7.  
  8. goog.ui.Tooltip = function(opt_elopt_stropt_domHelper) {
  9.   this.dom_ = opt_domHelper || (opt_el ?
  10.       goog.dom.getDomHelper(goog.dom.getElement(opt_el)) :
  11.       goog.dom.getDomHelper());
  12.  
  13.   goog.ui.Popup.call(thisthis.dom_.createDom(
  14.       'div'{'style''position:absolute;display:none;'}));
  15. };
  16.  
  17. goog.inherits(goog.ui.Tooltipgoog.ui.Popup);
  18.  
  19. goog.ui.Tooltip.activeInstances_ = [];
  20.  
  21. goog.ui.Tooltip.prototype.className = goog.getCssName('goog-tooltip');
  22.  
  23. goog.ui.Tooltip.prototype.attach = function(el) {
  24.   el = goog.dom.getElement(el);
  25.  
  26.   this.elements_.add(el);
  27.   goog.events.listen(elgoog.events.EventType.MOUSEOVERthis.handleMouseOver,falsethis);
  28. };

分析: 一个JS源文件包含如下几个部分

  • 自己能提供什么功能(类声明:使用goog.provide方法声明和注册一个类)
  • 提供这些功能需要额外的哪功能的支持(依赖声明:使用goog.require方法声明具体依赖的其它类)
  • 自身功能的具体实现(对象,类成员变量和成员方法声明和实现)
  • GO ON: 下面我们继续详细的分解每一部分的实现方式和细节

命名空间构造和注册(自己能提供什么功能)

说明: 下面是命名空间申明,声明一个js文件能提供哪些类,provide方法就是根据字符串创建一个JS对象,俗称命名空间

代码:

  1. //Tooltip 类声明
  2. goog.provide('goog.ui.Tooltip');
  3. goog.provide('goog.ui.Tooltip.State')// Enum 形式的对象
  4. // ==========更多的例子============
  5. //[goog/array/array.js]
  6.  
  7. goog.provide('goog.array');
  8. //[goog/dom/dom.js]
  9. goog.provide('goog.dom');// 命名空间声明,此命名空间下有变量和方法实现
  10. goog.provide('goog.dom.DomHelper');
  11. goog.provide('goog.dom.NodeType');
  12. //[goog/ui/combobox.js]
  13. goog.provide('goog.ui.ComboBox');
  14. goog.provide('goog.ui.ComboBoxItem')// Combobox的子项类

分析:

  • 一个文件可以当成一个包
  • 一个包中能声明(注册)多个类(多个类之间关系都比较密切[Tooltip 和 Tooltip.State],一般存在直接引用或者逻辑上是包含关系[Combobox和ComboboxItem],一般不超过三个)
  • 结合最上面的文件物理结构,我们可以发现类的命名空间和文件夹结构基本是一一对应的,但却不是完全对应。(如:goog.ui.Tooptip类对应文件结构为goog/ui/tooltip.js,而goog.ui.Tooltip.State就不是了对应goog/ui/tooltip/state.js,所以不是完全对应)
  • 根据第三点,下面会进一步分析google为什么没有直接利用这种对应关系以简化类到源文件的映射….
  • GO ON: 声明完自己能提供什么功能后,下面接着我们需要收集实现这些功能,我们还可以充分利用已经存在和实现的哪些功能,所以就需要加载这些功能所对应的文件包。

直接依赖包加载(提供这些功能需要额外的哪功能的支持)

说明: 声明完成类或者对象后,这里就需要去加载该包直接依赖的其它包了,所谓直接依赖,就是指在代码实现中有直接引用类或者对象的,比如 goog.Timer.clear(this.showTimer),这里就需要使用goog.require(‘goog.Timer’)加载goog.Timer所在的文件包了。

代码 :

  1. //Tooltip 依赖包加载
  2. goog.require('goog.Timer');
  3. goog.require('goog.array');
  4. // 类,依赖和文件之间的关系实现
  5.  
  6. goog.addDependency('ui/tooltip.js'['goog.ui.Tooltip','goog.ui.Tooltip.State']['goog.Timer''goog.array''goog.dom']);
  7. goog.addDependency('ui/tree/treenode.js'['goog.ui.tree.TreeNode'],['goog.ui.tree.BaseNode']);
  8. goog.addDependency('useragent/useragent.js'['goog.userAgent'],['goog.string']);
  9. 。。。。。。

分析:

  • 这里理论上可以加载任意多个直接依赖的文件包
  • 这里只是实现了包对包的依赖,并且这里使用的是异步加载机制,所以无法实现同步调用机制,即方法对包的依赖
  • googleClosure 提供一张很大的表达来存储类及其类所依赖的其它类,以及该类对应的物理文件路径
  • 再结合上面的物理文件结构和类声明部分,就大概知道为什么这里采用了非常庞大的一张表来存储类与JS源文件的映射关系,因为上面说的它的类名和文件夹结构不是完全对应的,如果完全对应的话,我们就不需要一张这么大的表来指明这种映射了,直接根据对应规则即可得到类的物理文件路径
  • 当然,有一张映射表,物理文件结构会更灵活一些,比如这里的多个关系比较密切的类可以申明到一个文件中

构造函数实现

说明: 这里很简单,就是使用最原始也最符合js本身实现的一种方式声明一个类

代码:

  1. //Tooltip 构造函数
  2. goog.ui.Tooltip = function(opt_elopt_stropt_domHelper) {
  3.   this.dom_ = opt_domHelper || (opt_el ?
  4.       goog.dom.getDomHelper(goog.dom.getElement(opt_el)) :
  5.       goog.dom.getDomHelper());
  6.  
  7.   goog.ui.Popup.call(thisthis.dom_.createDom(
  8.       'div'{'style''position:absolute;display:none;'}));
  9. };

分析:

  • 直接使用最原生的方式,简单直接明了,不从形式上去模仿Java后者其它后端语言,个人比较推荐
  • 参数规范
    • a.所有参数直接使用单个变量形式,不使用把可选参数放到一个options对象中
    • b.针对可选参数,在前面opt_前缀,标识此参数是可选参数
  • 使用call形式调用父类构造函数进行初始化,这里就能实现把父类实例对象的数据绑定到当前子类实例对象上,至于父类方法的绑定请继续看下面的继承声明和实现

继承声明和实现

说明: 上面是JS类构造函数的实现,其中有调用父类构造函数的实现,下面就如何继承父类进行分析,搞清楚这里就能明白父类方法绑定到子类的实现方式

代码:

  1. //Tooltip 继承实现
  2. goog.inherits(goog.ui.Tooltipgoog.ui.Popup);
  3. // 继承方法实现
  4. goog.inherits = function(childCtorparentCtor) {
  5.   /** @constructor */
  6.   function tempCtor() {};
  7.   tempCtor.prototype = parentCtor.prototype;
  8.   childCtor.prototype = new tempCtor();
  9.   childCtor.superClass_ = parentCtor.prototype;
  10.  
  11.   childCtor.prototype.constructor = childCtor;
  12. };

分析:

  • 通过设置子类的prototype,引入一个含有空构造函数的tempCtor类,实现父类纯方法的继承
  • 通过增加一个superClass变量指向父类的prototype,实现父类方法的直接调用,而不用担心子类重写了父类的方法,类似java的super调用
  • 通过增加constructor变量指向子类自己,实现Js原生数据类型的 constructor 引用
  • GO ON: 上面把一个包文件的结构部分基本分析清楚了,下面我们再继续分析一下一个类具体成员变量和成员方法的声明和实现的细节处理

属性声明

说明: 再接再励,继续分析类成员变量的声明和实现,这个基本比较简单,没什么难度,主要是需要说明一些书写规则和业内规范

代码:

  1. //Tooltip 成员变量
  2. goog.ui.Tooltip.activeInstances_ = [];
  3.  
  4. goog.ui.Tooltip.prototype.className = goog.getCssName('goog-tooltip');
  5. // 更多
  6. goog.ui.PopupBase.DEBOUNCE_DELAY_MS = 150;

分析:

  • 常量使用全大写,单词之间使用下划线分割
  • 很少的公有变量
  • 私有变量在后面添加下划线标识
  • 使用getter和setter访问私有变量
  • 没有保护权限的变量

方法实现

说明: 成员方法和成员变量基本一致

代码:

  1. //Tooltip 成员方法
  2. goog.ui.Tooltip.prototype.attach = function(el) {
  3.   el = goog.dom.getElement(el);
  4.  
  5.   this.elements_.add(el);
  6.   goog.events.listen(elgoog.events.EventType.MOUSEOVERthis.handleMouseOver,falsethis);
  7. };

分析:

  • 使用prototype声明方法
  • 私有方法和私有变量保持一致,在后面添加下划线标识
  • 公有方法直接声明
  • 没有保护权限的方法
http://ued.sohu.com/article/611
0 0