zoukankan      html  css  js  c++  java
  • JavaScript之面向对象与原型笔记整理--------继承(4)

    继承是面向对象中一个比较核心的概念,其他正统面向对象语言都会用两种方式实现继承:一个是接口实现,一个是继承。而ECMAScript只支持继承,不支持接口继承,而实现继承的方式依靠原型链完成。

    1.原型链实现继承

    function Box(){
          this.name = 'Lee';   //被继承的函数叫做超类型(父类,基类)
    }
    function Desk(){
          this.age = 100;       //继承的函数叫做子类型(子类,派生类)
    }
    function Table(){
    this.level = 'AAAAA';
    }
    //通过原型链继承,超类型实例化后的对象实例,赋值给子类型的原型属性 //new Box()会将Box构造里的信息和原型里的信息都交给Desk
    //Desk()的原型,得到的是Box的构造+原型里的信息 Desk.prototype = new Box(); Table.prototype = new Desk();
    var desk = new Desk(); console.log(desk.name); //Lee
    var table = new Table();
    console.log(table.name); //Lee
    console.log(table.age); //100

                                                                        原型链继承流程图

    注意:以上原型链继承还缺少一环,那就是Object,所有的构造函数都继承自Object。而继承Object是自动完成的,并不需要程序员手动继承。

    存在的情况:

    function Box(){
        this.name = 'Lee';
    }
    Box.prototype.name = 'Luck';
    function Desk(){
        this.age = 100;
    }
    Desk.prototype = new Box();
    
    var box = new Box();
    var desk = new Desk();
    
    console.log(desk.name);  //Lee   就近原则,实例里有就返回,没有就查找原型
    
    //子类型从属于自己活他的超类型
    console.log(desk instanceof Object);  //true 自动继承Object
    console.log(desk instanceof Desk);    //true
    console.log(desk instanceof Box);      //true
    console.log(box instanceof Desk);      //false

    继承也有之前问题,比如字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。

    为了解决引用共享超类型无法传参的问题,采用一种叫借用构造函数的技术,或者称为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。

    2.对象冒充继承

    //使用对象冒充继承
    function Box(name,age){
          this.name = name;
          this.age = age;
    }
    //若在此定义原型信息
    Box.prototype.family = '家庭';
    function Desk(name,age){    //对象冒充,对象冒充只能继承构造函数里的信息
          Box.call(this,name,age);
    }
    
    var desk = new Desk('Lee',20);
    console.log(desk.name);   //Lee
    console.log(desk.family );   //undefined 继承不到原型信息

    借用构造函数虽然解决了刚才两种问题,但没有原型,复用(里面有方法必须保持独立,构造函数里的方法,放在构造里,每次实例化,都会分配一个内存地址,浪费空间,所以最好放在原型里,保证多次实例化只有一个地址)则无从谈起。所以,需要原型链+借用构造函数的模式,这种模式称为组合继承。

    3.组合继承(应用广泛)

    原型链+借用构造函数的模式

     1 function Box(name,age){
     2     this.name = name;
     3     this.age = age;
     4 }
     5 
     6 Box.prototype.run = function(){
     7     return this.name + this.age;
     8 }
     9 
    10 function Desk(name,age){
    11     Box.call(this,name,age);   //对象冒充只继承构造函数里的信息   第二次调用Box
    12 }
    13 Desk.prototype = new Box();    //原型继承只继承原型里的信息   第一次调用Box

    14 var desk = new Desk('Lee',20);
    15 console.log(desk.run()); //Lee20 若没有第十三行,则继承不到,会报错

    解决的问题:

    (1)传参问题

    (2)原型链方法的继承

    (3)方法的共享

     4.原型式继承

    这种继承借助原型并基于已有的对象创建新对象,同时还不必因此创建自定义类型。

    //临时中转函数
    function obj(o){       //o表示将要传递进去的一个对象
        function F(){};    //F构造是一个临时新建的对象 
        F.prototype = o;  //将o对象实例赋值给F构造的原型对象
        return new F();    //最后返回这个得到传递过来对象的对象实例 
    }
    //F.prototype = o; 其实就相当于Desk.prototype = new Box();
    //这是字面量的声明方法,相当于var box = new Box();
    var box = {
        name:'Lee',
        age:20,
        family:['哥哥']
    }
    //box1就等于new F()
    var box1 = obj(box);
    console.log(box1.name);  //Lee
    box1.family.push('弟弟');
    var box2 = obj(box);
    console.log(box2.family); //哥哥,弟弟   引用类型共享了

    其实原型式继承就相当于原型链继承。仅此变了一下结构。

    缺点:引用类型共享了

    5.寄生式继承

    寄生式继承=原型式继承+工厂模式

    目的是为了封装创建对象的过程。

    //临时中转函数
    function obj(o){
        function F(){};
        F.prototype = o;
        return new F();
    }
    //寄生函数
    function create(o){
        var f = obj(o);
        f.run = function(){             //对F进行扩展
            return this.name + '!!';  
        }   
        return f;
    }
    var box = {
        name:'Lee',
        age:20,
    }
    var box1 = creat(box);
    console.log(box1.name);      //Lee
    console.log(box1.run());      //Lee!!

    此时,box1不仅仅拥有了box的属性和方法,还拥有了自己的run()方法。

    注意:使用寄生式继承方式来为对象添加函数,由于不能达到函数复用,导致效率变低,这与构造函数模式类似。

    组合式继承是JS最常用的继承模式,但也存在些问题,就是超类型在使用过程中被调用两次:一次是创建子类型的时候,另一次是在子类型构造函数的内部。

    寄生组合继承解决了两次调用的问题。

    6.寄生组合继承

     1 //临时中转函数
     2 function obj(o){
     3     function F(){};
     4     F.prototype = o;
     5     return new F();
     6 }
     7 //寄生函数
     8 function create(box,desk){
     9     var f = obj(box.prototype);
    10     f.constructor = desk;     //调整原型构造指针使得desk.constructor指向自己,不加这句的话,指向Box
    11     desk.prototype = f;
    12 }
    13 function Box(name,age){
    14     this.name = name;
    15     this.age = age;
    16 }
    17 Box.prototype.run = function(){
    18     return this.name + this.age +'!!';
    19 }
    20 function Desk(name,age){
    21     Box.call(this,name,age);  //对象冒充
    22 }
    23 //通过寄生组合继承老师先继承
    24 create(Box,Desk);
    25 var desk = new Desk('Lee',20);
    26 console.log(desk.run());      //Lee20!!
  • 相关阅读:
    C# Enum设计和使用的相关技巧
    Bat文件编写
    js cookie操作
    Android use custom html tag in TextView
    Charset , Encoding
    Android View (transship)
    Android About ContentProvider
    android,create text file in sdcard
    Eclipse Shortcuts
    Common Terminology
  • 原文地址:https://www.cnblogs.com/manru75/p/9484335.html
Copyright © 2011-2022 走看看