大纲
原型链继承
回到顶部
先说原型链继承的问题:包含引用类型属性的原型(不想被共享的引用属性)会变成共享。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
 | function SuperType(name){this.name = name;
 this.type = 'color';
 this.colors = ['yellow', 'white'];
 }
 SuperType.prototype.sayType = function() {
 console.log(this.type);
 };
 SuperType.prototype.sayName = function() {
 console.log(this.name);
 };
 
 
 function SubType(name){
 this.name = name;
 }
 SubType.prototype = new SuperType(this.name);
 
 var ins1 = new SubType();
 ins1.colors.push('red');
 console.log(ins1.colors);
 
 var ins2 = new SubType('issac');
 console.log(ins2.colors);
 ins2.sayType();
 ins2.sayName();
 
 | 

根据超类,colors应该为每个实例独有、不共享的,但由上面的代码可见,由于原型链继承后,colors变成了共享的属性
造成此结果的原因是:以SuperType的实例作为了prototype,而每个对象实例都只是指向这个prototype,因此每个实例拥有的colors实际都是同一个colors,换言之ins1.colors和ins2.colors存放都是同一个指针。
原型链继承的问题:无法保证“私有”引用类型继续“私有”
借调构造函数继承
回到顶部
为解决原型链本应为实例独有的引用属性变成共享属性的问题,提出借调构造函数继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | function SuperType(name){this.name = name;
 this.colors = ['yellow', 'white'];
 }
 SuperType.prototype.sayName = function(){
 console.log(this.name);
 }
 
 function SubType(name, age){
 SuperType.call(this, name);
 this.age = age;
 }
 
 var sub = new SubType('issac', 18);
 sub.sayName();
 
 | 

借调构造函数解决上面提出了的问题,而且接受参数更加的方便
构造函数自身的老问题:
由于构造函数会将它的把内容创建在一个新的作用域内赋给实例,因此,同样功能的方法就会为每个实例新建一份。这样本应共有的方法一旦多起来,创建的对象实例一旦多起来,就会造成很大冗余,这就是方法无法复用的问题。而且你也看到了,假如父类是使用组合式创建对象(组合原型式和构造函数式),子类是无法继承父类的原型的,因此上面 sub.sayName(); 才会抛出异常,因为subType中确实没有这个方法。
组合继承
回到顶部
由上面两种继承的方式可以看出: 原型链继承 不能保证“私有”引用属性继续“私有”,但是 借调构造函数 可以;借调构造函数继承 不可以复用方法,但 原型链继承 可以。那么可以组合两种方式的长处,使用 原型链继承 继承共享属性和方法,使用 借调构造函数 继承“私有”属性。
 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | function SuperType(name){this.name = name;
 this.colors = ['yellow', 'white'];
 }
 SuperType.prototype.sayName = function(){
 console.log(this.name);
 }
 function SubType(name, age){
 SuperType.call(this, name);
 this.age = age;
 }
 SubType.prototype = new SuperType();
 
 SubType.prototype.constructor = SuperType;
 SubType.prototype.sayAge = function(){
 console.log(this.age);
 }
 var ins1 = new SubType('a', 18);
 ins1.colors.push('red');
 console.log(ins1.colors);
 var ins2 = new SubType('b', 18);
 console.log(ins2.colors);
 
 | 
要理解组合继承是怎么工作的,要先知道一件事:js会先执行prototype的代码,再执行构造函数的代码。
首先是执行 SubType.prototype = new SuperType(); 这句相当于给SubType的原型对象创建了
| 12
 3
 4
 5
 6
 
 | {
 name: undefined,
 colors: ['yellow', 'white'],
 [[prototype]]: SuperType.prototype
 }
 
 | 
然后在新建SubType对象实例 **var ins1 = new SubType(‘a’, 18);**的时候调用构造函数,相当于给ins1对象创建“私有”属性:
| 12
 3
 4
 
 | var name = 'a';
 var age = 18;
 var colors = ['yellow', 'white'];
 
 | 
这些属性会屏蔽原型对象的同名属性,从而达到“私有”。
 
原型式继承
回到顶部
通过封装一个函数来作为继承的媒介
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | function object(o){function F(){}
 F.prototype = o;
 return new F();
 }
 var person = {
 name: "issac",
 friends: ['frank', 'Aye'],
 };
 var anthorPerson = object(person);
 anthorPerson.name = 'Annd';
 anthorPerson.friends.push('Dda');
 
 | 
在ES5中添加Object.create()方法规范了这种继承,这个方法接受两个参数,一个是原型对象,第二个是需要新增的属性会方法[可选]
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | var person = {name: "issac",
 friends: ['frank', 'Aye'],
 };
 var anthorPerson = Object.create(person);
 anthorPerson.name = 'Annd';
 anthorPerson.friends.push('Dda');
 var yetPerson = Object.create(person, {
 name: {
 value: 'issac'
 }
 });
 
 | 
Object.create()仅在IE9+支持,特别指出IE其他的浏览器就不说了
寄生式继承
回到顶部
寄生式继承是和原型式继承紧密相关的思路。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | function object(o){function F(){}
 F.prototype = o;
 return new F();
 }
 
 function createAnthor(o){
 var clone = object(o);
 clone.sayHi = function(){
 console.log('Hi Issac');
 }
 }
 var person = {
 name: "issac",
 friends: ['frank', 'Aye'],
 };
 var anthorPerson = createAnthor(person);
 anthorPerson.sayHi();
 
 | 
寄生的精髓即在原有对象上进行扩展
寄生组合式继承
回到顶部
在说组合继承的时候,已经说过超类是会被调用两次,“私有”属性的继承即通过对象实例的属性对原型对象的屏蔽,显然,原型对象中的“私有”属性是多余了。为解决中多余,提出了 寄生组合式继承
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | function object(o){function F(){}
 F.prototype = o;
 return new F();
 }
 function inheritPrototype(subType, superType){
 var prototype = object(superType.prototype);
 prototype.constructor = superType;
 subType.prototype = prototype;
 }
 function SuperType(name){
 this.name = name;
 this.colors = ['yellow', 'white'];
 console.log(Math.round(Math.random()*10));
 }
 SuperType.prototype.sayName = function(){
 console.log(this.name);
 }
 function SubType(name, age){
 SuperType.call(this, name);
 this.age = age;
 }
 
 inheritPrototype(SubType, SuperType);
 SubType.prototype.sayAge = function(){
 console.log(this.age);
 }
 var ins1 = new SubType('issac1', 18);
 ins1.colors.push('red');
 console.log(ins1.colors);
 
 | 

显然,使用寄生组合式继承仅仅调用了一次超类。
使用组合继承,会调用2次超类:
第一次,以超类的实例作为子类的原型对象(原型链式继承);
第二次,创建子类的对象实例时调用子类构造函数,子类构造函数,通过借调构造函数继承调用超类的构造函数;
 
寄生组合式继承是抹去上面说的第一次调用超类。它是通过直接将超类的原型对象赋值给子类的原型对象,然后子类在此基础上进行个扩充,这样就会不会触发到超类的构造函数。