一、传统方式:原型链实现继承(已废弃)

  1. Grand.prototype.LastName ="Tao"
  2. function Grand(){
  3. }
  4. var grand = new Grand;
  5. Father.prototype =grand;
  6. function Father (){
  7. }
  8. var father = new Father();
  9. Son.prototype = father;
  10. function Son(){
  11. }
  12. var son = new Son();

这种方式会把Father Grand 的其他属性方法都继承了,这样会导致效率、性能等问题,已经被我们废弃,但是作为时代的潜行者,这些还是需要了解的

二、构造函数的方式(借用别人的构造方法实现自己的功能)

通过call的方式改变this的内存空间,从而实现自己的功能,需要了解call的使用请移步https://www.yuque.com/taowuhua/gfneg0/wf15ni

  1. function Person(name,age,sex){
  2. this.name = name;
  3. this.age = age;
  4. this.sex = sex;
  5. }
  6. function Student (name,age,sex,color){
  7. Person.call(this,name,age,sex);//注意这里的写法
  8. this.color = color;
  9. }
  10. var student = new Student("taowuhua",18,"male","red");

缺点:
1)每次创建对象都要多走一个函数,一是调用了Student构造函数,二是调用了Person的构造函数。上面代码视觉上代码量减少了,但是性能方面是没有实质上的改变,方法都在构造函数中定义,因此函数的复用性就无从谈起了
2)不能继承构造函数的原型,这样只能实现原型属性的继承,不能继承原型的方法

三、公有原型(父子指向同一个内存空间,不建议使用)

Son的构造函数和Father的构造函数指向同一个原型,这种方式

  1. Father.prototype.lastName = "taowuhua"
  2. function Father (){
  3. }
  4. function Son(){
  5. }
  6. Son.prototype =Father.prototype;
  7. var son = new Son();

优化代码:封装一个公用函数

  1. Father.prototype.lastName = "taowuhua"
  2. function Father (){
  3. }
  4. function Son(){
  5. }
  6. function inherit(Target,Origin){
  7. Target.prototype =Origin.prototype;
  8. }
  9. inherit(Son,Father)
  10. var son = new Son();

优点:统一了原型,生产出的对象指向同一个内存地址
缺点:两个原型指向同一个内存空间,修改子类的数据,父类的数据也随之改变,这不是我们所期望的,现实生活中我们需要给子类加东西不要影响父类这是我们所期望的结果

四、圣杯模式(根据三的优化)

上述三,修改子类原型的数据会改变父类原型的数据,所以我们通过圣杯模式避免这种情况的发生
圣杯模式原理:创建一个函数作为中间枢纽
构造函数的原型和构造函数创建的对象分别指向Target.prototype 、Origin.prototype,然后通过原型链把他们四个的关系关联起来

  1. function inherit(Target, Origin) {
  2. function F() { }
  3. F.prototype = Origin.prototype
  4. Target.prototype = new F()
  5. Target.prototype.constructor = Target
  6. }

圣杯模式的演变解决了以下问题:

1)两个原型有联系但相互影响(三 构造函数的方式Target.prototype= Origin.prototype,相互影响)

解决方式:通过一个中间函数具有原型链的特性关联他们并且解耦

  1. function inherit(Target, Origin) {
  2. function F() { }
  3. F.prototype = Origin.prototype
  4. Target.prototype = new F()
  5. }

下图通过F.prototype 和new F()的作为原型链的存在将他们串起来了
image.png
上面的方法赋值之后Origin.prototype和F.prototype指向同一个内存空间,Target.prototype和new F()指向同一个内存空间,修改Origin.prototype的时候F.prototype会改变,因为原型链的存在导致new F()的内存地址的内容会改变,进而Target.prototype改变。但是修改Target.prototype内存地址的属性只会导致new F()的内存地址的属性会改变,原型链是不可逆的,从而解决了可以随意修改子类的属性而不用担心父类的会改变

2)构造函数紊乱

上述方式导致构造函数紊乱,使用Target.prototype.constrocuter 返回的是Origin构造函数,理应返回Target
解决方式:Target.prototype.constructor=Target

五、圣杯模式进阶版(借助闭包可以属性私有化)

闭包会把别人的作用域链变为自己的私有化变量。有些属性我们不想在某个位置被看到或者我们不想被外面定义的属性给覆盖了,可以考虑闭包的属性私有化,这样只能自己的函数能访问,外边的访问不到
例如:

  1. var inherit = (function () {
  2. var F = function F() { };
  3. return function (Origin,Target) {
  4. F.prototype = Origin.prototype
  5. Target.prototype = new F()
  6. Target.prototype.constructor = Target
  7. }
  8. }())

上面的 var F =function F(),被
function (Origin,Target) {
F.prototype = Origin.prototype
Target.prototype = new F()
Target.prototype.constructor = Target
}
函数持为私有化变量。进阶:https://github.com/YvetteLau/Step-By-Step/issues/35