正在重构整个框架,让命名空间对象dom也出于同一个继承体系下,就像mootools1.3的Type对象那样。
v2的目标大致如下:
//by 司徒正美 http://www.cnblogs.com/rubylouvre/ 2010.10.14 var PROTO = "prototype", CTOR = "constructor", A_proto = Array[PROTO], A_slice = A_proto.slice, to_s = Object[PROTO].toString, is = function (obj,type) { return (type === "Object" && obj === Object(obj)) || (type === "Array" && Array.isArray && Array.isArray(obj)) || // ECMA-5 15.4.3.2 (type === "Null" && obj === null) || (type === "Undefined" && obj === void 0 ) || obj && to_s.call(obj).slice(8,-1) === type; } function extend(target,source){ for(var name in source) if(source.hasOwnProperty(name) && !target[name]){ target[name] = source[name]; } return target; } //Object扩展 //fix ie for..in bug var _dontEnum = [ 'propertyIsEnumerable', 'isPrototypeOf','hasOwnProperty','toLocaleString', 'toString', 'valueOf', 'constructor']; for (var i in { toString: 1 }) _dontEnum = false; //第二个参数仅在浏览器支持Object.defineProperties时可用 extend(Object,{ create:function( proto, props ) {//ecma262v5 15.2.3.5 var ctor = function( ps ) { if ( ps && Object.defineProperties ) Object.defineProperties( this, ps ); }; ctor[PROTO] = proto; return new ctor( props ); }, keys: function(obj){//ecma262v5 15.2.3.14 var result = [],dontEnum = _dontEnum,length = dontEnum.length; for(var key in obj ) if(obj.hasOwnProperty(key)){ result.push(key) } if(dontEnum){ while(length){ key = dontEnum[--length]; if(obj.hasOwnProperty(key)){ result.push(key); } } } return result; } }); //用于创建javascript1.6 Array的迭代器 function iterator(vars, body, ret) { return eval('[function(fn,scope){'+ 'for(var '+vars+'i=0,l=this.length;i<l;i++){'+ body.replace('_', 'fn.call(scope,this[i],i,this)') + '}' + ret + '}]')[0]; }; //注释照搬FF官网 extend(Array[PROTO],{ //定位类 返回指定项首次出现的索引。 indexOf: function (el, index) { var n = this.length, i = ~~index; if (i < 0) i += n; for (; i < n; i++) if ( this[i] === el) return i; return -1; }, //定位类 返回指定项最后一次出现的索引。 lastIndexOf: function (el, index) { var n = this.length, i = index == null ? n - 1 : index; if (i < 0) i = Math.max(0, n + i); for (; i >= 0; i--) if (this[i] === el) return i; return -1; }, //迭代类 在数组中的每个项上运行一个函数,若所有结果都返回真值,此方法亦返回真值。 forEach : iterator('', '_', ''), //迭代类 在数组中的每个项上运行一个函数,并将函数返回真值的项作为数组返回。 filter : iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'), //迭代类 在数组中的每个项上运行一个函数,并将全部结果作为数组返回。 map : iterator('r=[],', 'r[i]=_', 'return r'), //迭代类 在数组中的每个项上运行一个函数,若存在任意的结果返回真,则返回真值。 some : iterator('', 'if(_)return true', 'return false'), //迭代类 在数组中的每个项上运行一个函数,若所有结果都返回真值,此方法亦返回真值。 every : iterator('', 'if(!_)return false', 'return true'), //归化类 javascript1.8 对该数组的每项和前一次调用的结果运行一个函数,收集最后的结果。 reduce: function (fn, lastResult, scope) { if (this.length == 0) return lastResult; var i = lastResult !== undefined ? 0 : 1; var result = lastResult !== undefined ? lastResult : this[0]; for (var n = this.length; i < n; i++) result = fn.call(scope, result, this[i], i, this); return result; }, //归化类 javascript1.8 同上,但从右向左执行。 reduceRight: function (fn, lastResult, scope) { var array = this.concat().reverse(); return array.reduce(fn, lastResult, scope); } }); //修正IE67下unshift不返回数组长度的问题 //http://www.cnblogs.com/rubylouvre/archive/2010/01/14/1647751.html if([].unshift(1) !== 1){ A_proto.unshift = function(){ var args = [0,0]; for(var i=0,n=arguments.length;i<n;i++){ args[args.length] = arguments[i] } A_proto.splice.apply(this, args); return this.length; //返回新数组的长度 } } //String扩展 var metaObject = { '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' },rquote = /[\x00-\x1f\\]/g; extend(String[PROTO],{ //javascript1.5 firefox已实现 quote:function () { var str = this.replace(rquote,function(chr){ var meta = metaObject[chr]; return meta ? meta : '\\u' + ('0000'+chr.charCodeAt(0).toString(16)).slice(-4); }); return '"'+ str +'"'; }, //ecma262v5 15.5.4.20 //http://www.cnblogs.com/rubylouvre/archive/2009/09/18/1568794.html trim: function(){ var str = this.replace(/^(\s|\u00A0)+/, ''), ws = /\s/, i = str.length; while (ws.test(str.charAt(--i))); return str.slice(0, i + 1); } }); //Math扩展 //http://www.cnblogs.com/rubylouvre/archive/2010/10/09/1846941.html var native_random = Math.random; Math.random = function(min, max, exact) { if (arguments.length === 0) { return native_random(); } else if (arguments.length === 1) { max = min; min = 0; } var range = min + (native_random()*(max - min)); return exact === void(0) ? Math.round(range) : range.toFixed(exact); } extend(Function[PROTO],{ //ecma262v5 15.3.4.5 bind:function(scope) { if (arguments.length < 2 && scope===void 0) return this; var fn = this, argv = arguments; return function() { var args = [], i; for(i = 1; i < argv.length; i++) args.push(argv[i]); for(i = 0; i < arguments.length; i++) args.push(arguments[i]); return fn.apply(scope, args); }; } }); function _numarr(s) { // 补零用的辅助函数 var r=[],k=-1,i=0,j,a=s.split(""),z=a.length; for(;i < z;++i){ for(j=0;j < z;++j){ r[++k]=a[i]+a[j]; } } return r; } var numarr = _numarr("0123456789"); function toISOString() { var ms = this.getUTCMilliseconds(), pad0 = (ms < 10) ? "00" : (ms < 100) ? "0" : ""; return this.getUTCFullYear() + '-' + numarr[this.getUTCMonth() + 1] + '-' + numarr[this.getUTCDate()] + 'T' + numarr[this.getUTCHours()] + ':' + numarr[this.getUTCMinutes()] + ':' + numarr[this.getUTCSeconds()] + '.' + pad0 + this.getUTCMilliseconds() + 'Z'; } extend(Date[PROTO],{ //ecma262v515.9.5.43 toISOString:toISOString, //ecma262v5 15.9.5.44 toJSON:toISOString }); extend(Date,{ //ecma262v5 15.9.4.4 now : function(){ return new Date().valueOf(); } });
全新的类工厂。
var oneObject = function(array,val){ var result = {},value = val !== void 0 ? val :1; for(var i=0,n=array.length;i < n;i++) result[array[i]] = value; return result; }, extendObject = oneObject(['_super',PROTO, 'extend', 'include','inherit','ancestors','parent']), includeObject = oneObject(['_super',CTOR]), classMethods = { inherit: function(parent) { if (parent && parent[PROTO]) { this[PROTO] = Object.create(parent[PROTO]);//高效设置原型链 this.parent = parent; } this.ancestors = []; while (parent) {//收集所有父类,用于构建方法链时查找同名方法 this.ancestors.push(parent); parent = parent.parent; } return this[PROTO][CTOR] = this; }, extend: function(){//扩展类成员 var parent = this.parent,names, name,n,method; arguments.length && A_slice.call(arguments).filter(function(module){ return is(module,"Object"); }).forEach(function(module){ names = Object.keys(module); n = names.length; while(n){ name = names[--n]; //避开FF为Object私自添加的原型属性toSource watch unwatch if(extendObject[name]===1) continue method = module[name]; this[name] = method; if(is(method,"Function") && parent){ method.parent = parent[name]; method._name = name; } } },this) return this; }, include:function(){//扩展原型成员 var parent = this.parent && this.parent[PROTO],names, name,n,method; arguments.length && A_slice.call(arguments).filter(function(module){ return is(module,"Object"); }).forEach(function(module){ names = Object.keys(module) n = names.length; while(n){ name = names[--n]; if(includeObject[name]===1) continue method = module[name]; this[PROTO][name] = method; if(is(method,"Function") && parent){ method.parent = parent[name]; method._name = name; } } },this) return this; } } function _super() {//构建方法链 var self= arguments.callee.caller,parent = self.parent; if(parent){ return parent.apply(this,arguments.length? arguments : self.arguments); }else{ throw 'this method "'+ self._name +'" no super method.'; } } function classPropsInject(klass,props){ ['extend','include'].forEach(function(name){ var modules = props[name]; if(is(modules,"Object") || is(modules,"Array")){ klass[name].apply(klass,[].concat(modules)); delete props[name]; } }) } function oop(obj){ obj = obj || {}; var superclass = obj.inherit; //父类 delete obj.inherit; var nonew = !!obj.nonew;//不用new关键字进行实例化 delete obj.nonew; var klass = function() { var that = this; if(that.singleton && klass.instance){ return klass.instance; } if(nonew && !(that instanceof klass)){ that = new klass; } that.init && that.init.apply(that,arguments); if(that.singleton){ klass.instance = that; } return that;//为nonew准备的出口 }; extend(klass,classMethods).inherit(superclass).extend(superclass); classPropsInject(klass,obj); klass._super = klass[PROTO]._super = _super; return klass.include(obj); }
扩展类成员以及在类方法中调用超类同方法:
var MyMath = oop({}); MyMath.extend({ PI:3.14, getPI:function(){ return this.PI; } }); var SonMath = oop({inherit:MyMath}); SonMath.extend({ getPI:function(){ return this._super()+0.0015926; } }); p(SonMath.getPI());
扩展原型成员,extend、include的属性可以是单个对象,也可以是对象数组。
var movable = { run:function(){ p("能跑") }, fly:function(){ p("能飞") } } var recognition ={ watch:function(){ p("看东西") }, smell:function(){ p("能嗅东西") } } var Robot = oop({ init:function(name,type){ this.name = name; this.type = name; }, include:[movable,recognition] }); var chi = new Robot("小叽","Chobits") ; p(chi.name); chi.watch(); chi.fly();
配置单例类
var God = oop({ init:function(name){ this.name = name; this.alertName = function(){ p(this.name) } }, singleton:true//注意这里,使用singleton属性 }); var god = new God("耶和华"); god.alertName(); //alerts 耶和华 var lucifer = new God("撒旦"); lucifer.alertName(); //alerts 耶和华 p(god === lucifer )//alerts true
配置不使用new关键字使可以实例化。
var dom2 = oop({ init:function(selector){ this.selector = selector return this; }, nonew:true, getSelectors : function(){ return (this.selector ||"").split(/\s+/) } }); p(dom2('.aaa .bbb .ccc').getSelectors())
继承示例1
var Animal = oop({ init:function(name){ this.name = name; }, getFood:function(){ return "各种各样的食物" }, extend:{ getClassName:function(){ return "Animal"; } } }); var Human = oop({ inherit:Animal, extend:{ getClassName:function(){ return this._super()+"-->人"; } } }); var me = new Human("john"); p(Human.getClassName()); p(me.getFood()); var Man = oop({ inherit:Human, init:function(name,sex){ this._super(); this.sex = sex; }, getFood: function(){ return this._super()+",尤其是肉类"; } }); var Genghis_Khan = new Man("成吉思汗","男"); p( Genghis_Khan.sex); p( Genghis_Khan.name); p( Genghis_Khan.getFood());
继承示例2
var Polygon = oop({ init:function(sides){ this.sides = sides }, getArea:function(){ return 0 //此只是个抽象类,不能用于具体计算 } }); p("==============Triangle===============") var Triangle = oop({ inherit:Polygon, init:function(base,height){ this._super(3); this.base = base; this.height = height; }, getArea:function(){ return 0.5*this.base*this.height; } }); var t = new Triangle(2,6); p(t.sides); p(t.getArea()); p("==============Rectangle===============") var Rectangle = oop({ inherit:Polygon, init:function(length,width){ this._super(4); this.length = length; this.width = width; }, getArea:function(){ return this.length*this.width; } }); var r = new Rectangle(7,6); p(r.sides); p(r.getArea(Rectangle)); p(r instanceof Polygon) p("==============Square===============") var Square = oop({ inherit:Rectangle, init:function(side){ this._super(side,side); } }) var s = new Square(6); p(s.sides); p(s.getArea()) p(s instanceof Polygon) p(s instanceof Square)