现代Web 前端的特征之一是由不止一个库所组成的,或者没有达到称为“库”的标准,有可能就是仅仅若干个小部件(Widget) 、某一段的JavaScript 代码(Snippets) 所组成。从开发者的角度而言,你所产生的代码有很大的机会与其他的代码产生冲突,因为我们清楚JavaScript 是一门格外*的语言,允许全局变量星罗棋布地存在,这样的话太多就极容易造成污染。所以有必要采用一定措施降低这种冲突发生的可能性。
图一 混合的脚本文件
extjs.com 的官方论坛它当前采用了来自三方面的程序(如上 图一 ):
- Ext 精减版(ext.js)
- Google 流量分析(urchin.js)
- 论坛自带的vRulletin 脚本(vbulletin_*.js)
如果有新需求,还可能加入其他第三方的JavaScript 库。进入Firebug 的Dom 选项卡观察,位于window 对象下面存放的变量数以百计,这些变量正是Ajax 库所带来到JavaScript 顶层全局环境的。JavaScript 是一门带有变量作用域的语言,任何一个变量若不加var 则可表示为一个全局成员,就加入在window 对象(即全局空间)下。
Ext 库并没有使用太多的全局变量在里面。任何一个它的类均安排在“Ext ” 对象底下,所以说整个框架只使用了一个全局变量Ext (如下 图)。
图二 Firebug 跟踪JavaScript 全局空间
命名空间(Namespace) 表示标识符(Identifier) 的上下文(Context) 。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他命名空间中。把实现的代码安排在一个特定的命名空间中,由各个类或单例构成,这样的JavaScript 编码风格不仅有良好的组织,直观可维护的优点,而且还能有避免它方代码干扰、窜改的好处。“为变量创建其命名空间,这样类就有了“安身之所”,不是飘荡四处的“全局变量。”(摘自API 文档)”。
提示: 笔者有一次就 Ext API 翻译的问题与参与人员发生小误会,有两份文件都是 DataField.js ,分别在不同的包中,一个是 /widget/DataField.js ;另外一个是 /widget/form/DataField.js 。但笔者误认为“未翻译的”当作“已翻译的”,这样大家碰头时就发生误会了。看来还真是命名空间相冲突而导致的结果,最后大家认真沟通下才把问题解释清楚。
使用Ext.ns() 可以实现简单的命名空间声明方式,其全称是Ext.namespace() 。Ext.ns() 用法如下 所示:
/* Ext.namespace会创建下列对象,若不存在的话 */ Ext.namespace('App', 'App.panel', 'App.data'); /* // 等价于注释的语句 if (!App) App = {}; if (!App.form) App.form = {}; if (!App.data) App.data = {}; */ /* 定义App.form包内部的新类SamplePanel */ App.panel.SamplePanel = Ext.extend(Ext.Panel, { initComponent: function(){ /*component configuration code here! */ App.form.SamplePanel.superclass.call(this); } });
Ext.namespace() 传入的参数中不能包含 ECMAScript 关键字,例如“ com.extjs.long ” 中的 long 是关键字,不能被 ns 方法执行。
Ext.namespace() 方法的关键地方之一是利用了eval 函数解析字符串为对象,若在一些不支持eval() 函数环境(如Adobe AIR 的安全沙箱下),可参考YUI 库的namespace() 方法,主要原理是JavaScript 的对象“索引式”声明增加Hash 表内元素,缺点是有第一个因子项的限制,因为该方法中须明确命名空间的第一个因子项。
// Yahoo! UI namespce方法源码 namespace = function() { YAHOO = {}; // YAHOO是全局变量 var a=arguments, o = null, i, j, d; // 遍历所传入的参数。JavaScript的参数具有参数个数不确定性 for (i=0; i<a.length; i=i+1) { d = a[i].split(".");// 返回数组,每个字符串以.分割开后,成为数组的元素 o = YAHOO; // 规定所有的命名空间均以YAHOO开头,如果依据显式声明则忽略 for (j=(d[0] == "YAHOO") ? 1 : 0; j<d.length; j=j+1) { o[d[j]]=o[d[j]] || {}; // 跳过当前已经存在的对象 o=o[d[j]]; // 移位到数组的下一个元素 } } return o; }
此处披露的内容是《Ext详解与实践》 的补充内容,完整的资料敬请参阅《Ext详解与实践》 一书(覆盖包含2.2/3.03的版本)的全面介绍。