YUI学习笔记(1)

by xujiwei (http://www.xujiwei.com/)

今天开始学习 YUI,加强一下对 JavaScript 的理解。

1. 命名空间 YAHOO.namespace(yahoo.js)

YUI 中使用了命名空间的概念,在 JS 中使用命名空间是为了模块以及代码组织清晰的需要,通过使用命名空间可以将功能相似或同一模块中的函数、变量等放到同一个命名空间下。

其实 JS 中的命名空间就是一个嵌套的对象而已,即子命名空间相当于父命名空间中的一个属性,它本身也是一个对象,这样子命名空间也可以有自己的子命名空间。

在 YUI 中,命名空间的格式与 C# 中类似,是以点号分隔的字符串,可以使用 YAHOO 对象的静态方法 namespace 来创建命名空间,需要注意的是,以 namespace 方法创建命名空间时,所有的对象都是附加在 YAHOO 这个对象上的,如果调用 namespace 方法创建一个“com.xujiwei.ajax”这样的命名空间,其中 ajax 的完整路径将是 YAHOO.com.xujiwei.ajax,而不是 com.xujiwei.ajax,也就是说 namespace 不会产生新的顶层对象,一切以 YAHOO 对象为基础。

namespace 方法接受一个或多个字符串来生成命名空间,但是它只返回最后一个参数所代表的命名空间的最后一个域的对象,例如使用 namespace("com.xujiwei.js", "com.xujiwei.ajax") 时它的返回值是代表 js 这个模块的的对象:

YAHOO.namespace("com.xujiwei.js", "com.xujiwei.ajax").get = function() {
alert("get method of com.xujiwei.ajax");
}
YAHOO.com.xujiwei.ajax.get();

2. YAHOO.lang.extend(yahoo.js)

YAHOO.lang.extend 负责扩展一个现在的函数对象,相当于面向对象中的继承,并且可以附加一个参数重写继承的方法,它的函数声明是:

extend: function(subc, superc, overrides)

其中 subc 为要扩展的函数对象,superc 是要继续的函数对象,overrides 中包含着要重写父类的方法。

YAHOO.lang.extend 是通过原型链的方式来扩展对象,即创建一个父类的实例做为子类的原型对象,在 extend 方法中,首先构造了一个空函数,将空函数的 prototype 指向父类的 prototype,以免在父类的构造函数需要参数时创建父类实例时会出错,然后创建这个空函数的实例赋值给子类的 prototype 对象,并且给子类添加一个 superclass 属性指定它的父类,另外还指定了子类 prototype 的 constructor 属性为子类构造函数,以免创建子类实例时会丢失 constructor 属性,这个问题我在《慎用 somefunction.prototype》有提到。

var F = function() {};
F.prototype = superc.prototype;
subc.prototype = new F();
subc.prototype.constructor = subc;
subc.superclass = superc.prototype;

接着处理的是要重写的函数,首先遍历 overrides 参数,并且使用 hasOwnProperty 方法判断属性是否为 overrides 自身的属性,而不是从 Object 对象的 prototype 继承过来的,如果是 overrides 自身的属性,那么就覆盖子类 prototype 的相同成员。

在处理 overrides 的最后,还修复了 IE 中不枚举与内部函数同名的函数的 bug,即例如 overrides 中包含了 toString 或 valueOf 的定义,但是这两个函数也是 Object 对象所具有的,在 IE 中使用 for(p in overrides) 就不会得到这两个函数,因此需要修复,这是 YAHOO.lang 中 _IEEnumFix 函数所做的工作。

if (overrides) {
for (var i in overrides) {
if (L.hasOwnProperty(overrides, i)) {
subc.prototype[ i ]=overrides[ i ];
}
}
L._IEEnumFix(subc.prototype, overrides);
}

_IEEnumFix 只是简单的判断当前浏览器是否为 IE,如果为 IE 则从一个已经定义的列表中获取在 IE 中枚举不到的函数名列表,逐一判断是否为源对象自身的属性并且与 Object 原型中的同名函数不一样,如果是则添加到目标对象。如果浏览器不是 IE 则 _IEEnumFix 直接就是一个空函数,直接不处理。

_IEEnumFix: (YAHOO.env.ua.ie) ? function(r, s) {
for (var i=0;i<ADD.length;i=i+1) {
var fname=ADD[ i ],f=s[fname];
if (L.isFunction(f) && f!=Object.prototype[fname]) {
r[fname]=f;
}
}
} : function(){},

3. YAHOO.lang.augmentObject(yahoo.js)

augmentObject 方法用来引入目标对象的属性,并且处理重写,通常用于合并配置对象与默认配置对象。

augmentObject 有三种调用方法,分别是:

augmentObject(result, source),一般情况下用些方法,从 source 引入 result 中没有的属性;

augmentObject(result, source, override),override 设置是否覆盖,如果是 true 的话,那么 source 中所有属性会覆盖 result 中已有的属性;

augmentObject(result, source, ovrride_name, ...),指定要覆盖属性的列表,source 参数后为要覆盖属性名称的列表,例如覆盖 result 中的 toString 和 valueOf,那么可以调用 augmentObject(result, source, "toString", "valueOf")来实现。

合并对象的最后同样调用了 _IEEnumFix 方法来修正 IE 中不枚举与内部属性同名属性的 bug。

augmentProto 与 augmentObject 功能基本相同,只不过后者是处理对象,而前者是处理函数的 prototype,它是通过使用两个参数的 prototype 来构造一个参数列表,传递给 augmentObject 对象来实现 augmentProto 的功能。

var a=[r.prototype,s.prototype];
for (var i=2;i<arguments.length;i=i+1) {
a.push(arguments[ i ]);
}
L.augmentObject.apply(this, a);

因为 augmentProto 是调用 augmentObject 来实现的,因此它的调用格式也是 augmentObject 一样,具体可以看看上面 augmentObject 的三种调用方法。

另外,YAHOO.lang.merge 方法也是通过调用 augmentObject 方法来实现对象的合并。