我们来模仿一下最OO的mootools的继承机制。它的类都有一个叫做initialize构造方法,这与Java的类都有一个与类名同名的构造方法一样的道理。只不过,这些叫initialize或init都是借鉴自Prototype,而Prototype那帮人是Ruby出身。为了区别mootools那种污染原生方法的做法,我把类的构造器命名为variant,并且禁止查看构造方法(像浏览器禁止查看原生对象的构造方法那样)。
var variant = function (options){ options = options || {}; var initialize = options.initialize || function(){}; var klass = initialize ; klass.constructor = arguments.callee; klass.prototype.constructor = klass; klass.toString = function(){//禁止查看构造方法 return “function variant(){\n [variant code]\n}” } return klass; }; var Person = variant({initialize:function(age){ this.age = age; }}); alert(Person)//看不到构造方法的实现 var p = new Person(3); alert(p) alert(p.age); alert(p.constructor);//看不到构造方法的实现 var P = variant({}) var a = new P; alert(a);
运行代码
var variant = function (options){ options = options || {}; var initialize = options.initialize || function(){}; var klass = initialize ; klass.constructor = arguments.callee; klass.prototype.constructor = klass; klass.toString = function(){//禁止查看构造方法 return "function variant(){\n [variant code]\n}" } return klass; };
这是一个非常简单的工厂方法,用于生产类的。options就是一属性包,可能装有我们的类的构造方法,我们要做的是把它提取出来,然后输送出去。
function factory(a){ a.b = b; a.c = c; return a; }
不过,这就有点像倒爷,我们应该像水果商,从果农收购水果回来包装一番,才卖出去。这包装就是往要生成的类添加各种原型方法与类方法。我们进一步打造我们的类工厂,让生产的类拥有继承能力,也就是把第一部分的makeBridge 加上去。
var variant = function (options){ options = options || {}; var initialize = options.initialize || function(){}; var superclass = options.inherit; delete options.initialize; delete options.inherit; var klass = initialize ; if(superclass){//如果是通过继承得来的属性 var bridge = function() {}; bridge.prototype = superclass.prototype; klass.prototype = new bridge; klass.prototype.superclass = superclass; } for(var i in options){//mixin klass.prototype[i] = options[i] } klass.constructor = arguments.callee;//类的静态属性 klass.prototype.constructor = klass;//真正用来创建实例的 klass.toString = function(){ return “function variant(){\n [variant code]\n}” } return klass; }; var Animal = variant({ initialize:function(name){ this.name = name; }, getName:function(){ return “这是” +this.name; } }); var a = new Animal(“动物”); alert(a.name) alert(a.getName()) var Tiger = variant({ inherit:Animal, initialize:function(name,age){ this.name = name; this.age =age; }, getAge : function(){ return this.age; }, setAge : function(age){ this.age = age; } }) var t = new Tiger(“老虎”,10); alert(t.age) alert(t.getName()) t.setAge(11); alert(t.getAge()); // alert(a.getAge());动物实例并没有此方法
运行代码
prototype继承是通过把子类的原型设置成父类的一个实例来进行继承的。因此无论是inherit也好,mixin也好,都是往子类的原型添加东西。打个比方,继承就是把一大堆属性与方法直接加在子类的原型上,mixin则相当于把一打属性与方法逐一加到构造函数的原型。不过在上面Tiger类的构造器写得有点不好,因为name属性本来父类就有,子类就不用定义一次。如果父类有许多实例属性,岂不是要写一大打赋值语句。应该改为
var Tiger = variant({ inherit:Animal, initialize:function(name,age){ this.superclass(arguments);//this.name = name this.age =age; }, getAge : function(){ return this.age; }, setAge : function(age){ this.age = age; } })
但是这种做法在第三代子类就行不通了,比如我们弄个子类叫IndiaTiger,它比Tiger多出一个类例属性location。
var IndiaTiger = variant({ inherit:Tiger, initialize:function(name,age,location){ this.superclass(arguments); this.location =location; } });
var variant = function (options){ options = options || {}; var initialize = options.initialize || function(){}; var superclass = options.inherit; delete options.initialize; delete options.inherit; var klass = initialize ; if(superclass){//如果是通过继承得来的属性 var bridge = function() {}; bridge.prototype = superclass.prototype; klass.prototype = new bridge; klass.prototype.superclass = superclass; } for(var i in options){//mixin klass.prototype[i] = options[i] } klass.constructor = arguments.callee;//类的静态属性 klass.prototype.constructor = klass;//真正用来创建实例的 klass.toString = function(){ return “function variant(){\n [variant code]\n}” } return klass; }; var Animal = variant({ initialize:function(name){ this.name = name; }, getName:function(){ return “这是” +this.name; } }); var Tiger = variant({ inherit:Animal, initialize:function(name,age){ this.superclass(arguments) this.age =age; }, getAge : function(){ return this.age; }, setAge : function(age){ this.age = age; } }); var IndiaTiger = variant({ inherit:Tiger, initialize:function(name,age,location){ this.superclass(arguments); this.location =location; } }); try{ var i = new IndiaTiger(“印度虎”,2,”印度”); alert(i.getName()); i.setAge(3); alert(i.getAge()); alert(i.location); }catch(e){ alert(“报错了”); alert(e); }
运行代码
当new印度虎实例时就报错了,除非其父类的构造器没有用到this.superclass(arguments)。不用说,问题是出自this,它的不确定性总是为我们惹很多麻烦。这里的this总为IndiaTiger 的实例,因此this.superclass总为Tiger ,也因此我们无法实例化Animal 。javascript的实例化过程是,有父类先实例化父类,然后再到下级子类。由于我们无法通过klass.prototype.superclass获取Animal,我们可以用klass.superclass来试一下。klass为类,而this为实例:
IndiaTiger.superclass.apply(this, arguments); //this为IndiaTiger 的实例 //arguments为IndiaTiger 构造器的参数对象
但我们不能把前面的IndiaTiger 写死,因为创建IndiaTiger 这个类时,它还不知自己叫IndiaTiger ,我们可以通过arguments.callee获取IndiaTiger自身。Tiger的构造相仿。
var IndiaTiger = variant({ inherit:Tiger, initialize:function(name,age,location){ arguments.callee.superclass.apply(this, arguments); this.location =location; } });
我们可以把它再抽取出来,这样每次就不用写这么长的代码了。
function _super(o, args) {//o为子类的实例,agrs为子类构造的arguments对象 return args.callee.superclass.apply(o, args); }
上面的代码已经假设了,它的构造器总会有inherit这个属性,但如果没有岂不是会报错,另,它还假设了我们的类上面有一个属性叫superclass,因此我们要在类工厂中做相应的调整。添加或修改如下两行代码:
var superclass = options.inherit || Object; klass.superclass = superclass; //类的superclass
function _super(o, args) {//o为子类的实例,agrs为子类构造的arguments对象 return args.callee.superclass.apply(o, args); } var variant = function (options){ options = options || {}; var initialize = options.initialize || function(){}; var superclass = options.inherit || Object; delete options.initialize; delete options.inherit; var klass = initialize ; var bridge = function() {}; bridge.prototype = superclass.prototype; klass.prototype = new bridge; klass.prototype.superclass = superclass;//实例的superclass for(var i in options){//mixin klass.prototype[i] = options[i] } klass.constructor = arguments.callee;//类的静态属性 klass.prototype.constructor = klass;//真正用来创建实例的 klass.superclass = superclass; //类的superclass // klass.toString = function(){ // return “function variant(){\n [variant code]\n}” // } return klass; }; var Animal = variant({ initialize:function(name){ this.name = name; }, getName:function(){ return “这是” +this.name; } }); var Tiger = variant({ inherit:Animal, initialize:function(name,age){ _super(this,arguments) this.age =age; }, getAge : function(){ return this.age; }, setAge : function(age){ this.age = age; } }); var IndiaTiger = variant({ inherit:Tiger, initialize:function(name,age,location){ _super(this,arguments); this.location =location; } }); var i = new IndiaTiger(“印度虎”,2,”印度”); alert(i.getName()); i.setAge(3); alert(i.getAge()); alert(i.location); alert(i.superclass)//我们暂时撤去toString方法,它将会弹出其父类的构造器initialize
运行代码
不过每次设置新类的构造器时都要添加一行_super(this,arguments)也太麻烦了,最好把它隐藏起来,内部调用。另,把_super方法放到工厂外,显得太松散,既然也是用来构建类,因此也该把它整合到类工厂中。
function factory(a){ var b = function(){ s(); a(); //*****其他方法 } return b }
也就是说,我们只要这样设置类的构造器即可:
var IndiaTiger = Variant({ inherit:Tiger, initialize:function(name,age,location){ this.location =location;// } });
<!doctype html> <html dir=”ltr” lang=”zh-CN”> <head> <meta charset=”utf-8″/> <title>类</title> <mce:script type=”text/javascript” charset=”utf-8″><!– window.onload = function(){ var Variant = function (options){ options = options || {}; var initialize = options.initialize || function(){}; var superclass = options.inherit || Object; var klassname = options.klassname ; delete options.initialize; delete options.inherit; var klass = function() { superclass.apply(this, arguments); initialize.apply(this, arguments) }; var bridge = function() {};//继承父类 bridge.prototype = superclass.prototype; klass.prototype = new bridge; klass.prototype.superclass = superclass;//实例的superclass klass.superclass = superclass; //类的superclass,用于创建父类的实例 for(var i in options){//mixin klass.prototype[i] = options[i] } klass.constructor = arguments.callee;//类的静态属性 klass.prototype.constructor = klass;//用于创建当前类的实例 var getKlassContext = function(c) { var e = c.toString().replace(/[\s\?]/g,””); getKlassContext = function() { return e; }; return getKlassContext(); }; var getKlassName = function(search,context){ var search = search.toString().replace(/[\s\?]/g,””), last = search.length >= 50 ? 50 :search.length; search = search.substring(0,last); var end = context.indexOf(search),start = end-100; start = start < 0 ? 0 :start; var str = context.substring(start,end); str = str.match(/var(\w+)\=Variant/); return (str && str[1]) ? str[1] :”Object”; }; if(!klassname){ context = getKlassContext(arguments.callee.caller); klassname = getKlassName(initialize,context); if(klassname == “Object”){ throw Error(“如果没有klassname就必须显式设置initialize”); } } klass.klassname = klassname; klass.prototype.klassname = klassname; klass.toString = function(){ return (“function “+klassname+”(){\n [variant code]\n}”); } return klass; }; var Animal = Variant({ initialize:function(name){ this.name = name; }, getName:function(){ return “这是” +this.name; } }); var Tiger = Variant({ inherit:Animal, initialize:function(name,age){ this.age =age; }, getAge : function(){ return this.age; }, setAge : function(age){ this.age = age; } }); var IndiaTiger = Variant({ inherit:Tiger, initialize:function(name,age,location){ this.location =location; } }); var i = new IndiaTiger(“印度虎”,2,”印度”); alert(i.klassname); alert(i.getName()); i.setAge(3); alert(i.getAge()); alert(IndiaTiger); alert(Tiger); } // –></mce:script> </head> <body> <pre> var Animal = Variant({ initialize:function(name){ this.name = name; }, getName:function(){ return “这是” +this.name; } }); var Tiger = Variant({ inherit:Animal, initialize:function(name,age){ this.age =age; }, getAge : function(){ return this.age; }, setAge : function(age){ this.age = age; } }); var IndiaTiger = Variant({ inherit:Tiger, initialize:function(name,age,location){ this.location =location; } }); var i = new IndiaTiger(“印度虎”,2,”印度”); alert(i.klassname); alert(i.getName()); i.setAge(3); alert(i.getAge()); alert(IndiaTiger); alert(Tiger); </pre> </body> </html>
运行代码
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容