• js面向对象之继承那点事儿根本就不是事


    继承

    说道这个继承,了解object-oriented的朋友都知道,大多oo语言都有两种,一种是接口继承(只继承方法签名);一种是实现继承(继承实际的方法)

    奈何js中没有签名,因而只有实现继承,而且靠的是原型链实现的。下面正式的说一说js中继承那点事儿


     1、原型链

    关于原型链在上一节面向对象之原型提了一些,这节好好说一说。

    原型链:实现继承的主要方法,利用原型让一个引用类型继承另一个引用类型的属性和方法。

    回顾:构造函数,原型,实例三者的关系

    每一个构造函数都有一个原型对象(Person.prototype);原型对象都包含指向构造函数的指针(constructor);每个实例都包含指向原型对象的指针(看不见的_proto_指针)

    原型链是怎么来的呢?

    某个构造函数的原型对象是另一个构造函数的实例;这个构造函数的原型对象就会有个(看不见的_proto_指针)指向另一个构造函数的原型对象;

    那么另一个原型对象又是其他的构造函数实例又会怎么样,就这样层层递进,形成原型链;来具体看一下吧

           //第一个构造函数;有一个属性和一个原型方法
           function SuperType(){
                    this.property=true;
           } 
           
           SuperType.prototype.getSuperValue=function(){
                   return this.property
           }
    
    
           //第二个构造函数;目前有一个属性
           function SubType(){
                    this.subproperty=false
           }
           
           //继承了SuperType;SubType原型成了SuperType的实例;实际就是重写SubType的原型对象;给SuperType原型对象继承了
           SubType.prototype=new SuperType()
           
           //现在这个构造函数有两个属性(一个本身的subproperty,一个继承的存在原型对象的property);两个方法(一个原型对象的getSubValue,一个原型对象的原型对象的getSuperValue)
           SubType.prototype.getSubValue=function(){
                   return this.subproperty
           }
           
           var instance=new SubType()    //创建第二个构造函数的实例
    
           console.log(instance.getSuperValue())   //true  先查找instance这个实例有没有此方法;显然没有,再查找SubType原型对象有没有此方法;也没有,再查找SubType原型对象的原型对象;显然是存在的

    注意:instance的constructor现在指向的是SuperType这个构造函数;因为原来的SubType.prototype被重写了,其内部的constructor也就随着SubType.prototype的原型对象的constructor指向构造函数SuperType;至于原型搜索机制是怎么样运行的,请仔细看上面的代码,相信你是可以的

    1.1完整的原型

    在原型那节已经提了些,还是再说一下。完整的原型包括Object。

    所有函数的默认原型都是Object的实例;每个默认原型都有个_proto_指针指向Object.prototype;因此自定义类型都继承如toString,valueOf的方法

    而Object.prototype的_proto_指针指向null来结束原型链。以Person构造函数为例,看看完整的原型链图

    1.2原型和实例的关系判断

    第一种使用instanceof操作符:  测试实例和原型链中出现的构造函数,结果为true

    第二种使用isPrototypeOf()方法: 只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型

           console.log(instance instanceof Object)      //都为true
           console.log(instance instanceof SuperType)
           console.log(instance instanceof SubType)
    
          
           console.log(Object.prototype.isPrototypeOf(instance))  //都为true
           console.log(SuperType.prototype.isPrototypeOf(instance))
           console.log(SubType.prototype.isPrototypeOf(instance))

    1.3谨慎定义方法

    注意:给原型对象添加方法,一定放在替换原型的后面,因为放在替换原型之前是找不到了,原型会被重写的;

    注意:在通过原型链继承时,不能使用对象字面量创建原型方法,因为也会重写原型链;

           function SuperType(){
                    this.property=true;
           } 
           
           SuperType.prototype.getSuperValue=function(){
                   return this.property
           }
           
           function SubType(){
                    this.subproperty=false
           }
           
           //继承SuperType
           SubType.prototype=new SuperType()
           
           //使用字面量添加新方法,导致上一行无效     因为现在的原型替换了Object实例而非SuperType的实例,关系中断
           SubType.prototype={
                  getSubValue:function(){
                          return this.subproperty;
                  },
                  somOtherMethod:function(){
                          return false
                  }
           };
    
           var instance=new SubType()
           console.log(instance.getSuperValue())    //error

    1.4原型链的问题

    1、包含引用类型值的原型:当实例是另一函数的原型时,引用类型值就会变成原型上的属性,就会被另一函数的实例所共享。

           function SuperType(){
                  this.colors=["yellow","red","olive"]
           }
    
           function SubType(){
           }
    
           SubType.prototype=new SuperType()    //color实际上就是原型上的了
    
           var instance1=new SubType()
           instance1.colors.push("purple")
           var instance2=new SubType()
    
           console.log(instance1.colors==instance2.colors)   //true

    2、创建子类型实例时,不能向超类型的构造函数传递参数(没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)

    2、借助构造函数

    为了解决原型中包含引用类型值带来的问题,利用构造函数来解决

    在子类型构造函数的内部调用超类型构造函数(函数是特定环境中执行代码的对象,可以通过apply或call调用)

           function SuperType(){
                   this.color=["yellow","red","olive"]
           }
    
           function SubType(){
                   //继承了SuperType
                   SuperType.call(this)
           }
    
           var instance1=new SubType()
           instance1.color.push("purple")
           var instance2=new SubType()
    
           console.log(instance1.color)   //["yellow","red","olive","purple"]
           console.log(instance2.color)   //["yellow","red","olive"]
    
    
           //传递参数
           function SuperType(name){
                  this.name=name
           }
           function SubType(){
                    SuperType.call(this,"double")
                    this.age=12
           }
    
           var instance1=new SubType()
           console.log(instance1.name)   //double
           console.log(instance1.age)    //12

    问题:仅仅借鉴构造函数,那么避免不了构造函数的问题,方法都在构造函数定义了,函数无法复用

    3、组合继承(常用的还是组合,和原型与构造结合一样)

           function SuperType(name){
                   this.name=name;
                   this.color=["yellow","red","olive"];
           }
    
           SuperType.prototype.sayName=function(){
                   console.log(this.name);
           }
      
           function SubType(name,age){
                   //继承属性,创建属性副本
                   SuperType.call(this,name);
                   this.age=age;
           }
           
           //继承属性和方法,只是原型中属性被后来的函数调用生成的属性副本遮盖
           SubType.prototype=new SuperType();
    
           alert(SubType.prototype.constructor)   //指向的是SuperType
    
           SubType.prototype.constructor=SubType; //将constructor回归到SubType构造函数身上
           SubType.prototype.sayAge=function(){
                    console.log(this.age)
           }
           
           
           var instance1=new SubType("double",23)
           instance1.color.push("pink")
           console.log(instance1.color)         //["yellow","red","olive","pink"]
           instance1.sayName()                  //double
           instance1.sayAge()                   //23
    
           var instance2=new SubType("single",34)
           console.log(instance2.color)         //["yellow","red","olive"]
           instance2.sayName()                  //single
           instance2.sayAge()                   //34

    还有其他的继承,花点时间写一下

    1、原型式继承

    克罗克福德写的;借助原型可以基于已有的对象创建新对象,同时不必创建自定义类型

           function object(o){           //本质上object()函数对其中对象的浅复制
                   function F(){}           //创建一个新的构造函数
                   F.prototype=o            //构造函数原型为传入的对象
                return new F()           //返回构造函数的实例
           }
    
           var person={
                   name:"double",
                   friends:["tom","jack","mike"]
           }
    
           var person1=object(person)      //事实上为原型共享
           person1.name="grey"
           person1.friends.push("single")
           
           console.log(person1.friends)    //["tom", "jack", "mike", "single"]
    
           var person2=object(person)
           person2.name="red"
           console.log(person2.friends)     //["tom", "jack", "mike", "single"]

    ES5为了规范原型式的继承,有个Object.create()来方便,IE9以上可以;只是想一个对象和另一个对象保持类似的情况,完全可以这种方法

           var person={
                   name:"double",
                   friends:["tom","jack","mike"]
           }
    
           var person1=Object.create(person)
           person1.name="single"
           person1.friends.push("singles")
    
           var person2=Object.create(person)
    
           console.log(person1.friends==person2.friends)  //true
    
           //Object.create()接受两个参数,一个为作为新对象原型的对象,一个为新对象定义额外属性对象
           var person={
                   name:"double",
                   friends:["tom","jack","mike"]
           }
    
           var person1=Object.create(person,{
                     name:{ 
                           value:"single"   //每个属性都是通过自己描述符定义的
                     }
           })

    2、寄生式继承

    思路和原型式继承一脉相承,创建一个用于封装继承过程的函数,内部通过方式增强对象,返回对象;主要考虑对象时使用

    function object(o){
                  function F(){}
                  F.prototype=o
                  return new F()
           }
    
           function createPerson(original){
                  var clone=object(original)     //继承原型
                  clone.sayName=function(){ 
                          alert("name")
                  }
                  return clone
           }
    
           var person={
                  name:"double",
                  friends:["single","tom","jack"]
           }
    
           var person1=createPerson(person)
           person1.sayName()    //name     引用类型值还是共享的

    3、寄生组合继承

    组合继承是继承中常常用到的,但是会调用两次超类型构造函数;寄生组合继承就是为了解决这个问题的

        function object(o){
              function F(){}
              F.prototype=o
              return new F()
        }
    
    
        function inheritPrototype(subType,superType){
              var prototype=object(superType)       //创建对象   (superType实例)
              prototype.constructor=subType         //增强对象
              subType.prototype=prototype           //指定对象   (原型赋予实例)
        }
        
    
         function SuperType(name,sex){
               this.name=name
               this.sex=sex
               this.colors=["red"]
         }
    
         SuperType.prototype.sayName=function(){
              alert(this.name)
         }
    
         function SubType(name,sex,age){
               SuperType.call(this,name,sex)
               this.age=age
         }
    
    
         inheritPrototype(SubType,SuperType)       //目前subType.prototype什么都没有
         SubType.prototype.sayAge=function(){      //为subType.prototype添加个方法
               alert(this.age)
         }
    
         var person1=new SubType("double","man",34)
         console.log(person1.name)    //SuperType  这是个Bug
         console.log(person1.sex)     //man
         console.log(person1.colors)  //["red"]
         person1.sayAge()             //34

    到此,差不多结束啦。

    各位客官们,关注一波呗

  • 相关阅读:
    一种通用的简易缓存设计方案
    SpringCloud接入Passport中台服务的FeignClient简易集成配置
    一种基于P2P技术的高效数据传输方式
    应用多环境部署和Redis高可用
    瑞金小吃
    前(单页面)后端完全分离的OAuth2授权和分享
    Session(数据)共享的前后端分离Shiro实战
    10万Http(单机和集群Server)Subscribe的可行性实验和压测
    2018年你应该了解的前端新技术
    js常见问题总结归纳
  • 原文地址:https://www.cnblogs.com/iDouble/p/8406975.html
Copyright © 2020-2023  润新知