基础二:javascript面向对象、建立对象、原型和继承总结(下)

前言:此次对上篇收个尾,主要总结一下javascript的继承。javascript

1.原型链

js中原型链是实现继承的主要方法。基本思想是:利用原型让一个引用类型继承另外一个引用类型的属性和方法。咱们来简单回顾一下之前的内容:java

  1. 每一个构造函数都有一个原型对象app

  2. 每一个原型对象都包含一个指向构造函数的指针:(constructor)函数

  3. 而实例和构造函数都有一个prototype属性指针指向原型对象。this

  4. 假如如今咱们让原型对象(A)等于另外一个类型的实例(b),此时至关于这个原型对象(A)总体做为一个实例指向另外一个实例的原型对象(b的原型对象B)spa

  5. 以上就实现了继承。prototype

看下面代码实例:指针

function SuperType(){
        this.property = true;    //property是SuperType的实例属性
    };
    
    SuperType.prototype.getSuperValue = function(){  //getSuperValue是SuperType的原型方法
        return this.property;
    };
    
    function SubType(){
        this.subproperty = false;
    }
    
    //让SuperType继承SubType
    SubType.prototype = new SuperType();
    
    SubType.prototype.getSubValue = function(){
        return this.subproperty;
    };
    
    var instance = new SubType();
    alert(instance.getSuperValue());      //true
  1. 在上面的代码中,定义了两个类型:SuperType和SubType。每一个类型分别有一个属性和方法。code

  2. 经过建立SuperType的实例,并赋值给了SubType.prototype,从而实现SubType继承了这个的实例,对象

  3. 原来存在于SuperType的实例中的全部的属性和方法,如今也存在于SubType.prototype中了。

  4. 既然如今SubType的原型对象SubType.prototype是SuperType的实例化对象,那么SuperType的实例属性property就位于SubType.prototype。以下图:

  5. 如今instance.constructor如今指向的是SuperType,图中能够看出来。也能够在进行继承以后,再进行以下步骤:
    SubType.prototype.constructor = Subtype;

原型链

2.完整原型链

全部函数的默认原型都是Object的实例,因此下图是上面例子的完整原型链。
完整原型链

3.重写或添加方法到超类型

(1)重写和添加方法必须在用超类型的实例(new SuperType())替换原型(SubType.prototype)以后。

function SuperType(){
        this.property = true;    
    };
    
    SuperType.prototype.getSuperValue = function(){  
        return this.property;
    };
    
    function SubType(){
        this.subproperty = false;
    }
    
    //让SuperType继承SubType
    SubType.prototype = new SuperType();
    //添加新方法
    SubType.prototype.getSubValue = function(){
        return this.subproperty;
    };
    //重写超类型中的方法
    SubType.prototype.getSuperValue = function(){
        return false;
    };

    var instance = new SubType();
    alert(instance.getSuperValue());      //false
    alert((new SuperType()).getSuperValue());   //我仿照java这么写,竟然返回true
  1. 重写超类型中的方法以后,经过SuperType的实例调用getSuperValue()时,调用的就是这个从新定义的方法。

  2. 经过SuperType的实例调用getSuperValue()时,调用的就是超类型中的方法,返回true

(2)经过原型链实现继承时,不能使用对象字面量建立原型方法,这样会重写原型链

function SuperType(){
        this.property = true;    
    };
    
    SuperType.prototype.getSuperValue = function(){  
        return this.property;
    };
    
    function SubType(){
        this.subproperty = false;
    }
    
    //让SuperType继承SubType
    SubType.prototype = new SuperType();
    
    SubType.prototype = {
        getSubValue: function(){
            return this.subproperty;
        },
        someOtherMethod: function(){
            return false;
        }
    };

    var instance = new SubType();
    alert(instance.getSuperValue());      //error

(3)原型链的问题

  1. 在经过原型链进行继承时,原型实际上会变成另外一个类型的实例,因此原先的实例属性也就变成了如今的原型属性了。

  2. 如今假如原型实例的属性是引用类型的,那么它会直接被添加成如今的对象原型的属性,那么经过这个建立的实例对这个引用类型的属性进行更改时,会当即反映在全部的实例对象上。

看下面代码:

function SuperType(){
        this.colors = ["red","blue","green"];    
    };
    
    function SubType(){
    }
    
    //让SubType继承SuperType
    SubType.prototype = new SuperType();
    
    var instance1 = new SubType();
    instance1.colors.push("black");
    alert(instance1.colors);       //["red","blue","green","black"]
    
    var instance2 = new SubType();
    alert(instance2.colors);        //["red","blue","green","black"]
  1. 当SubType经过原型链继承了SuperType以后,SubType.prototype就变成了SuperType的一个实例

  2. 此时SubType拥有一个本身的colors属性就像专门建立了一个SubType.prototype.colors属性同样

  3. 此时SubType全部的实例话对象都会共享这个colors属性,修改instances1的colors属性会当即在instances2中显示出来。

原型链还有一个问题:在建立子类型的实例时,不能向超类型的构造函数传递参数,其实是没有办法在不影响全部对象实例的状况下,给超类型的构造函数传递参数。

4.实现继承的其它方法

(1)借用构造函数

基本思想:

  1. 在子类型构造函数的内部调用超类型的构造函数,经过使用call()方法或者apply()方法。

例子:

function SuperType(name){
        this.name = name;
        this.colors = ["red","blue","green"];
    }
    
    function SubType(name,age){
        //继承了SuperType,同时还传递了参数
        SuperType.call(this,name);
        //再为子类型定义属性
        this.age = age;
    }
    
    var instance1 = new SubType("Jack");
    alert(instance1.name);
    instance1.colors.push("black");
    alert(instance1.colors);       //"red,blue,green,black"
    
    var instance2 = new SubType();
    alert(instance2.colors);        //"red,blue,green"
  1. 上述代码中解决了一个问题,就是引用类型的属性问题,每一个实例化的子类型都有本身的特有的属性

  2. 还存在一个问题,若是方法都定义在构造函数中,那么方法的就不能复用。

(2)组合继承-最经常使用的继承模式

组合继承的思路是:

  1. 使用原型链实现对原型属性和方法的继承,经过借用构造函数来实现对实例属性的继承

  2. 这样既经过在原型上定义方法实现了函数复用,又可以保证每一个实例都有它本身的属性。

例子:

function SuperType(name){
        this.name = name;
        this.colors = ["red","blue","green"];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    
    function SubType(name,age){
        //继承SuperType的属性
        SuperType.call(this,name);
        this.age = age;
    }
    
    //继承SuperType的方法
    SubType.prototype = new SuperType();
    //定义子类型本身的方法
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
    
    var instance1 = new SubType("Jack",26);
    instance1.colors.push("black");
    alert(instance1.colors);       //"red,blue,green,black"
    instance1.sayName();          //Jack
    instance1.sayAge();          //26
    
    var instance2 = new SubType("Rose",23);
    alert(instance2.colors);      //"red,blue,green"
    instance2.sayName();          //Rose
    instance2.sayAge();           //23

(3)原型式继承

思路:借助原型能够基于已有的对象建立新对象,还没必要所以建立本身的自定义类型
以下:

function object(o){
        function F(){};
        F.prototype = o;
        return new F();
    }
  1. object()函数内部先建立一个临时性的函数。

  2. 而后将传入的对象做为这个构造函数的原型。

  3. 最后返回这个临时类型的饿新实例。

以下:

var person = {
        name:"Jack",
        friends:["路人甲","路人乙"]
    };
    var anotherPerson = object(person);   //此处调用上方的object方法
    anotherPerson.name = "Rose";
    anotherPerson.friends.push("路人丙");
    
    var yetPerson = object(person);
    yetPerson.name = "Rick";
    yetPerson.friends.push("路人丁");
    
    alert(person.friends);      //["路人甲","路人乙","路人丙","路人丁"]

上述person.friends不只属于person全部,并且会被anotherPerson和yetPerson共享。
还有Object.create()方法,前面已经总结过了。

(4)寄生式继承

思路:建立一个仅用于封装继承过程的函数。

function createAnother(original){
        var clone = object(original);    //调用前面的object()方法
        clone.sayHi = function(){
            alert("hi");
        };
        return clone;
    }
    
    //使用
    var person = {
        name:"Jack",
        friends:["路人甲","路人乙","路人丙"]
    };
    var anotherPerson = createAnother(person);
    anotherPerson.sayHi();       //"Hi"

5.寄生组合式继承

(1)组合继承存在的问题

组合继承是js最经常使用的继承模式,不过它有本身的不足,组合继承最大的问题在于要调用两次超类型的构造函数一次是建立超类型的实例赋值给子类型的原型对象时一次是子类型构造函数内部
最终子类型会包含超类型对象的所有实例属性,可是咱们不得不在调用子类型构造函数时重写这些属性。

看下面例子:

function SuperType(name){
        this.name = name;
        this.colors = ["red","blue","green"];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    
    function SubType(name,age){
        SuperType.call(this,name);           //第二次调用
        this.age = age;
    }
    
    SubType.prototype = new SuperType();        //第一次调用
    SubType.prototype.constructor = SubType;
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
  1. 第一次调用SuperType构造函数时,SubType.prototype会获得两个属性:name和colors,它们都是SuperType的实例属性,只不过位于SubType的原型中。

  2. 当调用SubType的构造函数时,在函数内部又会调用SuperType的构造函数,又在新对象上建立了实例属性name和colors,因而这两个属性就屏蔽了原型中的同名属性。

(2)解决方法

寄生组合式继承的思想是:没必要为了子类型的原型而调用超类型的构造函数,咱们所须要的无非就是超类型的一个副本而已,本质上就是使用寄生式继承来继承超类型的原型,把返回的结果赋值给子类型的原型。

你们必定还记得上面说的原型式继承吧吧,将一个对象浅赋值给另外一个对象,如今也能够把一个超类型的原型赋值给另外一个子类型原型

1.回忆一下object()函数的代码

function object(o){
        function F(){}
        F.prototype = 0;
        return new F();
    }

2.建立一个函数,它接收两个参数:子类型构造函数和超类型构造函数。

function inheritPrototype(subType,superType){
        var prototype = object(superType.prototype);
        prototype.constructor = subType;
        subType.prototype = prototype;
    }
  1. 上面的代码第一步建立超类型原型的一个副本

  2. 为建立的副本添加constructor属性,弥补因重写原型而失去默认的constructor属性
    此处的重写发生在object()函数里面,超类型的原型superType.prototype直接赋给了F.prototype,而后object()函数又返回了F的新实例。

  3. 把建立新的对象赋值给子类型的原型

3.那么如今来使用一下

function SuperType(name){
        this.name = name;
        this.colors = ["red","blue","green"];
    }
    SuperType.prototype.sayName = function(){
        alert(this.name);
    };
    function SubType(name,age){
        SuperType.call(this,name);
        this.age = age;
    }
    inheritPrototype(subType,SuperType);
    SubType.prototype.sayAge = function(){
        alert(this.age);
    };
  1. 上述代码高效率,由于它只调用了一次SuperType的构造函数,所以避免了在SubType.prototype上面建立没必要要的、多余的属性,

  2. 此时原型链还能保持不变。

以上~~~~~

相关文章
相关标签/搜索