在js中,关于继承只有利用构造函数和原型链两种来现实。之前所见到的种种方法与模式,只不过是变种罢了。javascript
借用构造函数java
// 一个动物类,包含名字和性别属性 function Animal (name, sex) { this.name = name; this.sex = sex; this.getName = function(){ return this.name; }; } // Cat类继承Animal基类,而且拥有额外的属性 function Cat (name, sex, hasLegs) { this.hasLegs = hasLegs; Animal.apply(this, arguments);// 借用Animal的构造器 } // Dog类继承Animal基类,而且拥有与Cat类不同的额外的属性 function Dog (name, sex, otherFeatures) { this.otherFeatures= otherFeatures; Animal.apply(this, arguments); // 借用Animal的构造器 }
借用构造函数的优势就是可以复用代码;缺点就是它不能继承基类的原型,以及部分代码累赘。像Animal类中的getName方法,本该有一个就能够了,可是每次调用其构造器都会开辟新的空间来存放这个方法。若是把这些共有的属性或者方法放入原型链中,就不会须要每一个实例都有一个这样的属性或者方法,而是你们共用一个模板。app
构造函数与原型并用函数
// 从新定义动物类, function Animal (name, sex) { this.name = name; this.sex = sex; } // 提取公共的方法或者属性放入原型链中 Animal.prototype.getName = function (){ return this.name;} //Cat类不变,修改Cat的原型链,使其指向基类的原型 Cat.prototype = Animal.prototype; //Dog类不变,修改Dog的原型链,使其指向基类的原型 Dog.prototype = Animal.prototype;
测试代码1测试
// 分别new一个对象 var cat = new Cat('咪咪', 'female', true), dog = new Dog('汪汪', 'male', null); // 功能已实现 console.log(cat.getName(),dog.getName()); // 咪咪 汪汪 // 新的问题1 console.log(cat instanceof Cat, dog instanceof Cat); // true true 如今猫狗不分了 /*缘由是在改变各个具体子类的原型是,它们的构造器都指向了基类,它们拥有同一个构造器。 若是修改某个子类的原型constructor,必然会影响到其它子类*/ // 新问题2。若是如今Cat类的getName逻辑有变,不能修改基类的原型。现做出以下改动 function Cat (name, sex, hasLegs) { this.hasLegs = hasLegs; Animal.apply(this, arguments); // 新的逻辑 this.getName = function (){ return this.name+','+this.sex; } } //可是这样代码又不能达到复用,由于每一个Cat实例都有一个getName方法。 /*如何解决上述问题呢,也许你想到了——原型【链】。突出个链字,链说明是一节一节的,若是 咱们在子类与基类原型中间再加一节,不就完事了么*/ //定义一个空函数来作这个节点 function o (){} // 让‘空’节点指向基类的原型,Cat类再指向空节点 o.prototype = Animal.prototype; Cat.prototype = new o(); // 重置Cat的构造器指针 Cat.prototype.constructor = Cat; o.prototype = Animal.prototype; Dog.prototype = new o(); Dog.prototype.constructor = Dog;
完整的代码this
// 一个动物类,包含名字和性别属性 function Animal (name, sex) { this.name = name; this.sex = sex; } // 提取公共的方法或者属性放入原型链中 Animal.prototype.getName = function (){ return this.name;} function o (){} var oCat = new o(); // 修改Cat类的getName逻辑 oCat.getName = function (){return this.name+','+this.sex;} o.prototype = Animal.prototype; Cat.prototype = oCat; //重值Cat构造器指针 Cat.prototype.constructor = Cat; // 同上。而且这三行代码的顺序不能随意改动 o.prototype = Animal.prototype; Dog.prototype = new o(); Dog.prototype.constructor = Dog; // Cat类继承Animal基类,而且拥有额外的属性 function Cat (name, sex, hasLegs) { this.hasLegs = hasLegs; Animal.apply(this, arguments); } // Dog类继承Animal基类,而且拥有与Cat类不同的额外的属性 function Dog (name, sex, otherFeatures) { this.otherFeatures= otherFeatures; Animal.apply(this, arguments); } var cat = new Cat('咪咪', 'female', true), dog = new Dog('汪汪', 'male', null); // 功能正常,代码也达到进一步复用 console.log(cat.getName(), dog.getName()); // 如今猫是猫,狗是狗了 console.log(cat instanceof Cat, dog instanceof Cat); // 两个子类的构造器也是对的了 console.log(cat.constructor, dog.constructor);
如今彷佛完整了,但是好像仍是有些遗憾。如同被妹子拒了同样:你人很好,咱们仍是作朋友吧。言外之意就是还没好到让妹子想跟你在一块儿的程度。那么哪里不够呢?如今只有两个子类,若是有几十个的话,仍是要作不少重复的工做;若是又有一个机械的基类,又要作一样的事情。那么,咱们能够把这个继承的方法写成面向对象的形式么?答案是:能够滴。prototype
// 将继承的实现细节用函数包裹起来,classPropers是须要覆盖的属性和方法 function inherit(classPropers){ var o = function (){}, // 空函数用作空节点 parent = this, // 这里的this表明基类构造器 child = function(){}, // 返回一个子类 hasOwnConstructor = false; // 是否拥有本身的构造器 if(typeof classPropers === 'object' && classPropers.hasOwnProperty('constructor')){ //若是有构造器属性,则覆盖构造器 child = function (){ classPropers.constructor.apply(this,arguments); } hasOwnConstructor = true; }else{ // 不然使用基类的构造器 child = function(){ parent.apply(this, arguments); } } o.prototype = parent.prototype; child.prototype = new o(); if(hasOwnConstructor){ // 重置构造器指针 child.prototype.constructor = classPropers.constructor } if(classPropers){ /*$.extend是jQ函数,这里再也不实现。若是classPropers与基类有相同的方法,则会‘覆盖’ 基类的方法*/ $.extend(child.prototype, classPropers); } // 继承基类的静态方法,这样子类还能够被继承 $.extend(child, parent); child.__super__ = parent.prototype; // 以防万一之后还要调用基类相同方法 return child; }
完整测试代码2指针
// 一个动物类,包含名字和性别属性 function Animal (name, sex) { this.name = name; this.sex = sex; } Animal.prototype = { getName: function(){ return this.name;}, getSex: function(){ return this.sex;} }; function inherit(classPropers){ var o = function (){}, // 空函数用作空节点 parent = this, // 这里的this表明基类构造器 child = function(){}, // 返回一个子类 hasOwnConstructor = false; // 是否拥有本身的构造器 if(typeof classPropers === 'object' && classPropers.hasOwnProperty('constructor')){ //若是有构造器属性,则覆盖构造器 child = function (){ classPropers.constructor.apply(this,arguments); } hasOwnConstructor = true; }else{ // 不然使用基类的构造器 child = function(){ parent.apply(this, arguments); } } o.prototype = parent.prototype; child.prototype = new o(); if(hasOwnConstructor){ // 重置构造器指针 child.prototype.constructor = classPropers.constructor } if(classPropers){ /*$.extend是jQ函数,这里再也不实现。若是classPropers与基类有相同的方法,则会‘覆盖’ 基类的方法*/ $.extend(child.prototype, classPropers); } // 继承基类的静态方法,这样子类还能够被继承 $.extend(child, parent); child.__super__ = parent.prototype; // 以防万一之后还要调用基类相同方法 return child; } Animal.inherit = inherit; var Cat = Animal.inherit({sayHi:function(){console.log('喵喵...')}}), cat = new Cat('咪咪', '不告诉你'); console.log(cat.getName(),cat.getSex()); var Dog = Animal.inherit({ constructor:function(name){ this.name = name; console.log('我有本身的工厂(构造器)'); } }), dog = new Dog('我为本身代言'); console.log(dog.getName(),dog.constructor); // 老虎小时候就是猫,不信,我有证据以下。 var Tiger = Cat.inherit({constructor:function(){console.log('出来一声吼啊!喵喵......咋变猫叫了呢?wuwu...')}}), tiger = new Tiger(); tiger.sayHi();
记得引用jQuery或者本身实现$.extend函数。对象