js原型链继承有很多种实现的方式,主要介绍以下3种:
一、拷贝继承:
顾名思义拷贝就是利用for in 或者jq的extend,把一个构造函数的prototype循环遍历赋值给另一个构造函数的prototype,再把构造函数内的属性也一起继承过去
var Base = function(name){ this.name = name; }; Base.prototype.getName = function(){ alert(this.name); }; var Aaa = function(name,sex){ //继承属性 Base.call(this,name); this.sex = sex; }; //继承方法 extend(Aaa.prototype,Base.prototype); //调用: var a = new Aaa('小明','男'); a.getName();
二、类式继承:
- 构造函数A.prototype = new 构造函数B();
- 继承属性
- 修改构造函数指向
var Base = function(){ this.cacheName = []; }; Base.prototype.addName = function(name){ this.cacheName.push(name); }; Base.prototype.getNames = function(){ alert(this.cacheName); }; var Aaa = function(){ //继承属性 Base.call(this); }; //继承方法 Aaa.prototype = new Base(); //修改构造函数指向 Aaa.prototype.constructor = Aaa; //调用: var a = new Aaa(); a.addName('小明'); a.getNames();
三、原型继承:
利用es5新特性Object.create实现原型继承
Object.create = Object.create || function(o){ var F = function(){}; F.prototype = o; return new F(); };
Object.create的作用是只继承方法不继承属性
与类式继承一样需要继承属性、修改构造函数指向
var Base = function(name){ this.name = name; }; Base.prototype.getName = function(){ alert(this.name); }; var Aaa = function(){ //继承属性 Base.apply(this,arguments); }; //继承方法 Aaa.prototype = Object.create(Base.prototype); //修改构造函数指向 Aaa.prototype.constructor = Aaa; var a = new Aaa('小明'); a.getName(); Object.create = Object.create || function(o){ var F = function(){}; F.prototype = o; return new F(); };
通用的原型链继承:
Object.create = Object.create || function(o){ var F = function(){}; F.prototype = o; return new F(); }; function Class(){}; Class.extend = function(opts){ //父类的原型链 var _super = this.prototype; var prototype = Object.create(_super); for(var prop in opts){ var val = opts[prop]; prototype[prop] = val; } var Fn = function(){ this.init && this.init.apply(this,arguments); }; //继承方法 Fn.prototype = prototype; //修改构造函数指向 Fn.prototype.constructor = Fn; //把父类的所有方法保存到超类 Fn.prototype._super = Object.create(_super); //递归调用Class.extend方法 Fn.extend = arguments.callee; return Fn; };
调用:
var Person = Class.extend({ init:function(opts){ opts = opts || {}; this.name = opts.name; this.sex = opts.sex; this.age = opts.age; }, getName:function(){ return this.name; }, getSex:function(){ return this.sex; }, getAge:function(){ return this.age; } }); var Student = Person.extend({ init:function(opts){ opts = opts || {}; this.job = opts.job; this._super.init.call(this,opts); }, getJob:function(){ return this.job; }, getInfo:function(){ alert('name:' + this.getName() + ',sex:' + this.getSex() + ',age:' + this.getAge() + ',job:' + this.getJob()); } }); var student = new Student({ name:'小明', sex:'男', age:18, job:'学生' }); student.getInfo();
我们发现子类调用父类的方法有些不方便:this._super.init.call(this,opts);
var Student = Person.extend({ init:function(opts){ opts = opts || {}; this.job = opts.job; this._super.init.call(this,opts); }, ....................... });
而且这种添加超类的方式性能也很差。如果子类没有调用超类,那么创建超类就是一种浪费。
Fn.prototype._super = Object.create(_super);
利用闭包改写Class.extend方法:
Object.create = Object.create || function(o){ var F = function(){}; F.prototype = o; return new F(); }; var fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; function Class(){}; Class.extend = function(opts){ //父类的原型链 var _super = this.prototype; var prototype = Object.create(_super); for (var prop in opts) { var val = opts[prop]; prototype[prop] = typeof val == "function" && typeof _super[prop] == "function" && fnTest.test(val) ? (function(prop, fn){ return function() { var tmp = this._super; //在调用fn方法前创建一个超类 this._super = _super[prop]; //该方法只需要暂时绑定,所以在执行完毕后将其移除 var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(prop, val) : val; } var Fn = function(){ this.init && this.init.apply(this,arguments); }; //继承父级的所有方法 Fn.prototype = prototype; //修改构造函数指向 Fn.prototype.constructor = Fn; //递归调用Class.extend方法 Fn.extend = arguments.callee; return Fn; };
调用:
var Student = Person.extend({ init:function(opts){ opts = opts || {}; this.job = opts.job; this._super(opts); }, ................... });
现在调用也很方便了,无需修正this的指向。
当调用原型方法前创建超类,调用后即销毁它。而且超类只保存了对应父类的一个方法,并不是保存父类所有的方法。
(function(prop, fn){ return function() { var tmp = this._super; //在调用fn方法前创建一个超类 this._super = _super[prop]; //该方法只需要暂时绑定,所以在执行完毕后将其移除 var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(prop, val)