第一类工厂相当于mootools的Native方法,用于创建一些具有扩展能力的类,但这些类并没有继承能力,此类工厂也不能以某个类作为蓝本创建子类。总之,一切从简。第一类工厂要依赖一些辅助方法,一并写出来了。
var dom = {};//命名空间
dom.mixin = function (obj, bag) {
if (arguments.length === 1) {
bag = obj;
obj = this;
};
var nil = {};
if (obj && bag && typeof bag === 'object') {
for (var p in bag) {
if (nil[p] === void 0 || nil[p] != bag[p]) obj[p] = bag[p];
}
};
if (!+"/v1") {
p = bag.toString;
if (typeof p === "function" && p !== obj.toString && p !== nil.toString && p !== "/nfunction toString() {/n [native code]/n}/n") {
obj.toString = bag.toString;
}
}
return obj;
};
dom.factory = function(obj){//
var klass = obj.klass,
init = obj.init;
klass.fn = klass.prototype = {
init :init,
constructor: klass
};
klass.fn.init.prototype = klass.fn;
delete obj.klass;delete obj.init;
dom.mixin(klass.fn, obj);
//用于扩展原型方法
klass.mixin = function(bag){
dom.mixin(this.fn,bag);
return this;
};
klass.alias = function(oldName, newName){
var bag = {};
if (dom.isString(oldName) && dom.isString(newName)){
var method = this.fn[oldName]
if (!!method){
bag[newName] = method;
return this.mixin(bag);
};
};
//如果是一个属性包,如Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});
bag = oldName;
for (var name in bag)
if(bag.hasOwnProperty(name))
this.alias(name,bag[name]);
return this;
};
klass.staticizeWithout = function(arr){
var conditions = {},keys = arr || [],me = this;
for(var i=0,n = keys.length;i<n;i++){
conditions[keys[i]] = 1;
}
dom.each(me.fn,function(method, name){
if(!conditions[name] && dom.isFunction(me.fn[name]) && dom.isUndefined(me[name])&&
name !== 'init' && name !== 'toString' && name !== 'valueOf' ){
me[name] = function () {
var args = dom.toArray(arguments),
caller = args.shift();
method.name = name; //为其泛化方法添加一个name属性
return method.apply(me(caller), args);
}
}
});
return me;
}
return klass;
}
dom.mixin({
is : function(obj,type) {
return Object.prototype.toString.call(obj).match(/^/[object/s(.*)/]$/)[1] === type;
},
isArray: function (obj) {
return dom.is(obj,"Array");
},
isFunction: function (obj) {
return dom.is(obj,"Function") ;
},
isNumber: function (obj) {
return dom.is(obj,"Number") ;
},
isString: function (obj) {
return dom.is(obj,"String") ;
},
isUndefined: function (obj) {
return obj === void(0);
},
isWindow:function(obj){
return obj.document && obj.document.nodeType === 9 ||
obj.contentDocument && obj.contentDocument.nodeType === 9
},
each: function (obj, fn, bind) {
for (var key in obj) //只遍历本地属性
if (obj.hasOwnProperty(key))
fn.call(bind, obj[key], key, obj);
},
toArray: function( array ) {
var ret = [];
if ( array != null ) {
var i = array.length;
if ( array.length == null || dom.isString(array) || dom.isFunction(array) || dom.isWindow(array)) {
ret[0] = array
} else {
while(i)
ret[--i] = array[i];
}
}
return ret;
}
});
这样dom就有如下几个方法mixin,factory,isArray,isFuction,isNumber,isString,isUndefined,isWindow,toArray,each与is,走实用主义路线,像isObject基本没有用。最新版的jQuery1.4a2好像加了两个特别的验证方法,isPlainObject(验证其是javascript core的Object而非宿主对象或DOM对象)与isEmptyObject(空对象,有点像{},但要保证其原型没有被扩展)。
哈希正是本文要说的,虽然javascript的Object什么都是,但显然力不从心嘛,像mootools,Prototype与Base2都搞了一个Hash类,我觉得jQuery也应该有一个,起码它来构建jQuery.data比较爽一些。
var hash = dom.factory({
klass:function(obj) {
if(obj instanceof arguments.callee) return obj;
return new arguments.callee.fn.init(arguments);
},
//支持多种创建方式
//[1]传入一个普通对象
//hash({ key: "val" }) ==> { key: "val" }
//[2]传入两个参数,第一个必须是字符串
//hash("key", nasami) ==> { key: nasami }
//[3]传入一个以逗号隔开的字符串
//hash("key,a,key2,b") ==> { key: "a", key2: "b" }
//[4]传入一个以空白隔开的字符串
//hash(" key a key2 b") ==> { key: "a", key2: "b" }
//[5]传入一个数组
//hash([aa,bb,cc]) ==> { 1:aa,2:bb,3:cc }
init: function(obj) {
var key = obj[0],
value = obj[1],
core = {},
i = 0;
if(obj.length === 2){//如果有两个参数
core[key] = value;
}else{
if(dom.isString(key)){
key = key.replace( /^/s+|/s+$/g, "");//进行trim操作
var arr = key.indexOf(",") !== -1 ? key.split(",") : key.split(//s+/g);
while ((value = arr[i++]))
core[value] = arr[i++];
}else if(dom.isArray(key)){
for(var i= 0,n=key.length;i<n;i++)
core[i] = key[i]
}else{
core = key;
}
};
this.empty();
if(core)
this.update(core);
},
empty: function() {
this._hash = {};
this.length = 0;
return this;
},
//用于初始化hash
//把普通对象的键值利用put方法传入_hash中,不考虑其prototype的成员
update: function(obj) {
for(var prop in obj)
if(obj.hasOwnProperty(prop))
this.put(prop, obj[prop]);
return this;
},
contains: function(key) {
return this.get(key) !== void(0);
},
put: function(key, value) {
if(!this.contains(key)) {//如果没包含则
this.length++;
}
this._hash[key] = value;
return value;
},
//取得相应的值
get: function(key) {
return this._hash[key];
},
//移除一个键值对
remove: function(key) {
delete this._hash[key];
this.length--;
return this;
},
//移除指定的键值对,并返回对应的值
pop: function(key) {
var results = this.get(key);
this.remove(key);
return results;
},
//取得所有的键,以数组形式返回
keys: function() {
var keys = [],obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop))
keys.push(prop);
return keys;
},
//取得所有的值,以数组形式返回
values: function() {
var values = [],obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop))
values.push(obj[prop]);
return values;
},
//取得所有的键值对,以数组形式返回
items: function() {
var items = [],obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop))
items.push([prop, obj[prop]]);
return items;
},
//变成普通对象
toObject: function() {
return this._hash;
},
//仅当此键不存在时才添加,
ensure: function(key, value) {
var results = this.get(key);
if(results === void(0))
return this.put(key, value);
return results;
},
forEach: function(fn, bind){
var pairs = this.items();
for(var i=0,n=pairs.length;i<n;i++){
fn.call(bind, pairs[i][1], pairs[i][0]);
}
},
map: function(fn, bind){
var results = hash({});
this.each(function(value, key){
results.put(key, fn.call(bind, value, key));
});
return results;
},
filter: function(fn, bind){
var results = hash({});
this.each(function(value, key){
if (fn.call(bind, value, key))
results.put(key, value);
});
return results;
},
index: function(val) {//与get方法相反,取得其key
var obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop) && obj[prop] === val)
return prop;
return null;
},
toString:function(){
var pairs = this.items(),results = [];
for(var i=0,n=pairs.length;i<n;i++){
results[i] = pairs[i][0]+":"+pairs[i][1]
}
return "{ "+results.join(", ")+" }";
}
});
hash.alias("forEach","each");
hash.staticizeWithout();
dom.hash = hash;
一些测试代码:
对于某些人看来,第一类工厂也有太过臃肿,为生成一个对象太劳师动众了(我一并写出来是为了方便我测试类工厂),下面是一个精简版。不需要类工厂,自行构建哈希,不过少了一些静态方法:
var hash = function(obj) {
if(obj instanceof arguments.callee) return obj;
return new arguments.callee.fn.init(arguments);
};
hash.fn = hash.prototype= {
init: function(obj) {
var key = obj[0],
value = obj[1],
core = {},
toString = Object.prototype.toString,
i = 0;
if(obj.length === 2){//如果有两个参数
core[key] = value;
}else{
if(toString.call(key) === '[object String]'){
key = key.replace( /^/s+|/s+$/g, "");//进行trim操作
var arr = key.indexOf(",") !== -1 ? key.split(",") : key.split(//s+/g);
while ((value = arr[i++]))
core[value] = arr[i++];
}else if(toString.call(key) === '[object Array]'){
for(var i= 0,n=key.length;i<n;i++)
core[i] = key[i]
}else{
core = key;
}
};
this.empty();
if(core)
this.update(core);
},
empty: function() {
this._hash = {};
this.length = 0;
return this;
},
//用于初始化hash
//把普通对象的键值利用put方法传入_hash中,不考虑其prototype的成员
update: function(obj) {
for(var prop in obj)
if(obj.hasOwnProperty(prop))
this.put(prop, obj[prop]);
return this;
},
contains: function(key) {
return this.get(key) !== void(0);
},
put: function(key, value) {
if(!this.contains(key)) {//如果没包含则
this.length++;
}
this._hash[key] = value;
return value;
},
//取得相应的值
get: function(key) {
return this._hash[key];
},
//移除一个键值对
remove: function(key) {
delete this._hash[key];
this.length--;
return this;
},
//移除指定的键值对,并返回对应的值
pop: function(key) {
var results = this.get(key);
this.remove(key);
return results;
},
//取得所有的键,以数组形式返回
keys: function() {
var keys = [],obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop))
keys.push(prop);
return keys;
},
//取得所有的值,以数组形式返回
values: function() {
var values = [],obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop))
values.push(obj[prop]);
return values;
},
//取得所有的键值对,以数组形式返回
items: function() {
var items = [],obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop))
items.push([prop, obj[prop]]);
return items;
},
//变成普通对象
toObject: function() {
return this._hash;
},
//仅当此键不存在时才添加,
ensure: function(key, value) {
var results = this.get(key);
if(results === void(0))
return this.put(key, value);
return results;
},
forEach: function(fn, bind){
var pairs = this.items();
for(var i=0,n=pairs.length;i<n;i++){
fn.call(bind, pairs[i][1], pairs[i][0]);
}
},
map: function(fn, bind){
var results = hash({});
this.each(function(value, key){
results.put(key, fn.call(bind, value, key));
});
return results;
},
filter: function(fn, bind){
var results = hash({});
this.each(function(value, key){
if (fn.call(bind, value, key))
results.put(key, value);
});
return results;
},
index: function(val) {//与get方法相反,取得其key
var obj = this._hash;
for(var prop in obj)
if(obj.hasOwnProperty(prop) && obj[prop] === val)
return prop;
return null;
},
toString:function(){
var pairs = this.items(),results = [];
for(var i=0,n=pairs.length;i<n;i++){
results[i] = pairs[i][0]+":"+pairs[i][1]
}
return "{ "+results.join(", ")+" }";
},
each:this.forEach
}
hash.fn.init.prototype = hash.fn;
哈希对象 | |
---|---|
empty | 清空hash。 |
contains | 检测hash是否包含此键值对,参数为key |
put | 向hash增加一个键值对,参数两个,key与value |
get | 根据key取得相应的value |
remove | 根据key移除相应的键值对,返回修改后的hash |
pop | 根据key移除相应的键值对,返回被移除的value |
keys | 取得所有的键,以数组形式返回 |
values | 取得所有的值,以数组形式返回 |
items | 取得所有的键值对,以数组形式返回 |
toObject | 变成普通对象 |
ensure | 仅当此键不存在时才添加新的键值对 |
forEach或each | 类似Array.forEach,迭代执行传入函数 |
map | 类似Array.map,迭代执行传入函数,把执行结果放到一个新hash返回 |
filter | 类似Array.filter,迭代执行传入函数,把符合条件的键值对放到一个新hash返回 |
toString | 没什么好说的 |