这篇博客是面向对象编程的第三篇,JS继承。继承顾名思义,就是获取父辈的各类"财产"(属性和方法)。html
咱们的JavaScript比较特别了,主要经过原型链实现继承的。编程
下面介绍各类实现继承的方式:原型链继承,借用构造函数,组合继承,原型式继承,寄生式继承,寄生组合式继承。app
原型咱们都知道,每一个构造函数都有一个原型对象(prototype),用于存放共享的属性方法。
原型链继承原理:咱们要继承一个父类,那就把这个子类的原型对象指向父类的实例就好了。框架
//建立父类构造函数 function Father(){ this.fristName = "Father"; } //给父类的原型对象添加方法 Father.prototype.getFatherName = function(){ return this.fristName ; } //建立子类构造函数 function Son(){ this.name = 'jack'; } //重点这行:把子类的原型对象指向父类实例实现继承 Son.prototype = new Father(); //咱们还能够为子类添加原型方法 Son.prototype.getName = function(){ return this.name; } //测试一下,建立一个子类的实例 var s1 = Son(); s1.getFaterName(); //Father 继承了爸爸的名字了!
1.上面咱们看到为子类原型添加方法getName,它在子类原型对象指向父类实例后,也必定要在这句话后面,若是在前面的话,这方法会被覆盖的。由于子类原型对象指向父类实例时至关于重写子类原型对象。
2.为子类原型添加方法式不能用字面量的方式,这样会重写已经继承类父类的原型对象。函数
很明显的,继承的属性方法都在子类的原型对象里面,那么里面的属性方法都是共享的,这明显不是符合咱们日常要求。测试
借?是的,借一下父类的构造函数,怎么借?那就使用call或者apply方法吧。
借用构造函数原理: 就在子类建立构造函数时内部调用父类的构造函数。优化
//父类(父类是相对来讲的,你们注意一下噢) function Father(){ this.colors = ['red','green']; } //子类 function Son(){ //重点这行:内部调用父类构造函数,this是指向将来建立Son的实例 Father.call(this); } //测试一下,继承了父类的属性了,而且属性不会共享噢 var s1 = new Son(); s1.colors.push('blue'); console.log(s1.colors); ['red','green','blue'] //s2实例的colors不会被影响 var s2 = new Son(); console.log(s2.colors); ['red','green']
这里的借用构造函数能够实现属性和方法不会被共享,由于属性在构造函数中,而不是在原型对象中。可是这也说明这种方式没办法共享方法的。this
组合继承=原型链继承+借用构造函数继承,没错就结合两种方法就行了。
组合继承原理:实例共享的属性方法:我就原型链继承;实例私用的属性方法:我就借用构造函数(结合二者的优势)prototype
//父类 function Father(name){ this.name = name; this.colors = ['red','green']; } //父类原型对象 Father.pototype.sayName = function(){ console.log(this.name); } //子类 function Son(name,age){ //借用父类构造函数 Father.call(this,name); this.age = age; } //子类原型指向父类实例,原型链继承 Son.prototype = new Father(); Son.prototype.constructor = Son; //让子类的原型只向子类构造函数 Son.prototype.sayAge = function(){ console.log(this.age); } //建立实例对象 var s1 = new Son('Mc ry',22); var s2 = new Son('yy',20); //继承了父类方法,sayName是共享的 s1.sayName(); //mc ry s2.sayName(); //yy //继承colors,每一个实例私有 s1.colors.push('blue'); console.log(s1.colors); ['red','green','blue'] console.log(s2.colors); ['red','green']
可能有的疑惑:在原型链继承那里,子类原型对象指向类父类的实例,应该继承了全部的属性和方法啊,那应该都是共享的,但为何colors,name属性不会共享呢?
缘由:在调用借用构造函数时,属性在子类新实例中建立了,也就是在子类实例中已经有的父类属性就不用继续到原型对象中查找了,也就是屏蔽了,因此不会共享了。code
这是道格拉斯·克罗克福提出的一个方式,他提出一个函数object() 来简单实现不用构造函数的继承
//object函数,传入一个对象 function object(o){ //建立一个空的构造函数 function F(){}; //这个空构造函数的原型对象指向o这个对象 F.prototype = o ; //返回F的实例对象 return new F(); }
认真读这段代码能够知道,这个函数最终返回一个对象,这个对象拥有传入对象o的所有属性和方法。从而实现了继承。
//一个对象 var person = { name : 'ry', sayName : function(){ console.log(this.name); } } //children对象继承了person对象全部的属性方法 var children = object(person);
原型式继承方式的出现,能够不用建立构造函数都能继承另外一个对象的属性方法。可是很明显,所用属性都是共享的。
ES5中有个object.create()方法的做用和上面的object()同样的。
寄生式其实和利用了原型式,都要使用到object函数,但不一样的是寄生式中新的对象可以添加本身的方法。
function creatAnother(o){ //调用object继承o var obj = object(o); //还能够添加本身想要的方法 obj.sayHi = function (){ console.log('hi'); } //返回这个对象 retrun obj; } //新的对象继承了person var anotherPerson = creatAnother(person); //继承后能使用person的方法 anotherPerson.sayHi(); //"hi"
上面组合式方式中虽然是最经常使用的,但有追求的仍是会继续优化它。由于组合方式中也有不够好的地方:
一方面:咱们能够看到调用了两次父类的构造函数。(一次是原型链继承中子类原型对象指向父类实例时,一次是借用构造函数中)
另外一方面:就是上面疑惑,子类的原型中拥有父类已经有所有属性,但咱们又要在调用子类构造函数时重写部分属性。
因此寄生组合方式就解决了上面,经过一个函数来代替组合方式中的原型链继承。
//主要是这个函数,实现子类原型对象只继承父类原型对象中的属性 function inheritPrototype(subType,superType){ //利用上面的Object函数,将父类的原型赋予prototype变量 var prototype = object(superType.prototype); //将prototype的构造函数从新指向子类 prototype.constructor = subType; //将prototype给sub的原型对象 subType.prototype = prototype; } //将前面的组合继承改写 //父类 function Father(name){ this.name = name; this.colors = ['red','green']; } //父类原型对象 Father.pototype.sayName = function(){ console.log(this.name); } //子类 function Son(name,age){ //借用父类构造函数 Father.call(this,name); this.age = age; } //(这行用inheritPrototype函数替换)子类原型指向父类实例,原型链继承 inheritPrototype(Son, Father); Son.prototype.sayAge = function(){ console.log(this.age); }
寄生组合式的继承方式是最理想的方式,它使得子类构造函数继承父类构造函数的属性,子类的原型对象继承父类原型对象的属性和方法。
1.此次博客讲述了在js中是如何实现继承的,有不少中方式,但主要的是组合方式和寄生组合方式。继承后咱们可以使用父类的属性和方法,增长了代码的重用性。
2.了解js继承做用:有助于咱们去阅读一些框架的源码,可能本次代码有点难以理解,我打上了大量的注释,供你们一块儿阅读,仍是那句话,多看几遍,其义自见。若是以为有收获,就点个赞吧,关注我吧。
同系列几篇:
第一篇:JavaScript--我发现,原来你是这样的JS(一)(初识)
JS--我发现,原来你是这样的JS:面向对象编程OOP[1]--(理解对象和对象属性类型)
JS--我发现,原来你是这样的JS:面向对象编程OOP[2]--(建立你的那个对象吧)
我发现,原来你是这样的JS所有文章汇总(点击此处)
本文出自博客园:http://www.cnblogs.com/Ry-yuan/ 做者:Ry(渊源远愿) 欢迎转载,转载请标明出处,保留该字段。