其他静态成员
另外,你需要给Y.Base.create()的第五个参数上挂载两个静态成员,如果你开发一个插件(plugin),你需要传入NS(命名空间Namespace的缩写)参数,否则插件就无法工作,NS是一个字符串,作为在宿主对象中的一个属性,插件将挂载其上,不过你要特别小心,选择NS命名的时候不要覆盖已有的插件命名。
如果你开发一个需要渐进增强的组件(widget),你需要传入HTML_PARSER参数,HTML_PARSER是一个object,其成员可以是从配置参数传入的已有的HTML片段获取,也可以来自于一个(CSS3)选择器或者一个getter函数。请参考Widget使用指南中的Progressive Enhancement来了解更多。
当然,你有时会提供一些类静态成员(属性/方法),并暴露给开发者去调用,而类中的常量由于闭包的保护对外并不可见,如果你想对外暴露他们,则需要通过Y.Base.create()的第五个参数传入一个boject,object的成员都会被当作静态成员挂载在类本身上。例如WidgetStdMod中的HEADER,BODY和FOOTER常量就是用这种方法声明的静态常量,不过你需要通过Y.WidgetStdMod.BODY来访问。
实例成员
Y.Base.create()的第四个参数(object)会mix到类原型上,通常我们先声明属性后声明方法,没有原因一定要这样,只是惯例,声明的顺序也不影响程序的执行。这样约定是为了更工整的组织你的代码,尽管你在类构造器中也可以创建成员,但还是强烈建议先声明后赋值,声明的位置用来为APIdoc补充注释,这样的coding方式大大增强源码的可读性。
私有属性通常带有下划线前缀, 类的公共接口最好通过Attribute传入,而不是直接挂载实例属性,直接挂载属性很不安全,毕竟它不像通过Attribute可以带入校验器、适配参数类型(通过setter函数),并且可以支持AOP的一些特性诸如触发valuechange事件等,你会发现通过Attribute传参的好处不胜枚举。
在使用Attribute的时候,注意不要将成员初值设为对象或数组,因为它们都会指向同一个object而使程序出错,最好将那些object成员初值设为null,也不要将属性留空,如果实在不知道成员的数据类型,统统设置为null,因为在调试程序的时候,引用那些没有初值的成员会报一个“类型错误”。
Base中的实例方法
你可能会注意到,在子类中并没有定义类构造器,因为在构建实例的时候,Base会通过调用initializer方法(如果存在的话)对实例进行初始化,Initializer使用的参数即是实例的构造参数,所以可以把initializer看做构造器,所有派生自Base的类在创建之时先要被Base“扩充”(Argument)一下,Base这里被称之为“摻元类”,参元类在调用initializer之前通过Attribute来传入参数,对于设置了HTML_PARSER的Widget来说, Attribute中的属性是从HTML“标签”中获取,而不是直接从参数中读的。
Initialize方法干了这么几件事情,首先是将所有需要初始化的属性设置为object或array,然后注册(publish)自定义事件,虽然EventTarget可以直接在没有注册事件的情况下fire事件,但我还是建议先注册事件类型(为了事件可以冒泡),同样,类似先声明后赋值的原则,你可以在注册事件的地方补充APIDoc注释,乍一看似乎在函数体内写APIDoc注释很奇怪,但你也找不到更好的写APIDoc注释的方式。
接下来就是处理Initializer函数接受的参数,你也可以在参数中增加配置属性中没有的内容,比如我们将on,after, buggleTargets和plugins等Base原本没有的属性(或方法)挂到Base上(参见Base),也可以给Widget父类挂载新的子属性,尽管你在实例化一个对象的时候只传入一个参数,但这个参数所包含的内容不止与此。
Javascript没有析构函数的概念。Base允许你声明一个名为destructor的函数来释放内存资源,依此来模拟析构函数。但这只是一个折衷方案,在删除一个对象时,javascript编译器不会自动回收其内存,因此对于没用的对象是需要手动销毁的,即调用一下析构函数。
开发者在使用你的类的时候,不需要直接调用initializer和destructor,Base会在必要的时候调自动用他们。Initializer会在对象实例化时调用,destuctor会在用户调用destroy方法销毁对象时调用。
绑定的事件没有被解绑常常会造成内存泄漏。Widget会自动解除组件内部绑定的事件,其他的手动绑定的事件不会被自动解除。Base也不能解除所有事件监听,我通常使用这段代码来解除事件绑定,先在声明私有成员_eventHandlers: _eventHandles: null,
然后在initializer方法内将_eventhandlers设为数组 initializer: function (cfg) { this._eventHandles = []; // … … },
同样在initializer 函数(或者在Widget的bindUI函数)中,通过这段代码来绑定监听: this._eventHandles.push(this.after(‘someAttributeChange’, this._afterSomeAttributeChange, this));
在destructor方法内: destructor: function () { Y.each(this._eventHandles, function (handle) { handle.detach(); }); },
这里在initializer中,有可能绑定了自定义事件(除了UI元素上的事件),就像上文提到的,Attribute是处理参数的标准方式。任何非数据的存储都由事件处理逻辑完成。在上面的例子中,我使用_afterSomeAttributeChange来监听someAttribut的属性变化。事件发生会触发回调,并带入即事件对象(我通常以ev表示),事件对象包含几个与事件相关的属性,其中一个属性newVal表示变化后的值。
出处:Taobao.com UED Team
责任编辑:bluehearts
上一页 使用YUI 3开发Web应用的诀窍 [3] 下一页 使用YUI 3开发Web应用的诀窍 [5]
◎进入论坛网页制作、WEB标准化版块参加讨论,我还想发表评论。
|