记录高级js学习(十)通过原型链实现继承

44 阅读2分钟

继承:

原型链实际上就是一种继承,对象直接查找属性或者方法的时候,是会顺着原型链查找的

      // 原型链实现继承的弊端
      // 1.打印p1对象,某些属性看不到(继承的属性)
      // 2.修改父类的属性时,例如数组的push,会影响到不同的子类
      // 3.Student原型中的constructor属性丢失
      // 父类
      function Person(){
        this.name = "张三"
      }
      Person.prototype.eating = function(){
        console.log(this.name,"在吃饭");
      }
      // 子类
      function Student(){
        this.sno = 100
      }
      Student.prototype = new Person()
      Student.prototype.studying = function(){
        console.log("学习考了",this.sno);
      }
      var p1 = new Student()
      p1.eating()  //会顺着原型链向上查找eating方法
      // 借用构造函数实现的弊端
      // 1.Person函数至少被调用两次
      // 2.p1的原型上会多一些值为undefined的属性
      // 父类
      function Person(name,age){
        this.name = name
        this.age = age
      }
      Person.prototype.eating = function(){
        console.log(this.name,"在吃饭");
      }
      // 子类
      function Student(name,age,sno){
        Person.call(this,name,age) //借用构造函数,给p1赋值
        this.sno = sno
      }
      Student.prototype = new Person()
      Student.prototype.studying = function(){
        console.log("学习考了",this.sno);
      }
      var p1 = new Student("张三",18,100)
      console.log('p1: ', p1);
      console.log('p1: ', p1.__proto__); //p1的原型会多两个undefined的属性

想要获得一个继承于obj的空对象的三种方法:

      var obj = {
        name:"李斯",
        age:18
      }
      // 方法1,通过setPrototypeOf()方法
      function creatObj1(o){
        var newObj = {}
        Object.setPrototypeOf(newObj,o)
        return newObj
      }
      // 方法2:通过new构造函数的方法
      function creatObj2(o){
        function Fn(){}
        Fn.prototype = o
        var newObj = new Fn()
        return newObj
      }
      // 方法3,通过Object.create方法
      function creatObj3(o){
        var newObj = Object.create(o)
        return newObj
      }

最终方案是

      //寄生组合式继承
      //借用工厂函数创建一个原型为o的空对象
      function createObject(o){
        function Fn(){}
        Fn.prototype = o
        var newObj = new Fn()
        return newObj
      }
      //将创建的空对象赋值给子类的原型对象,实现了继承
      function inheritPrototype(subType,superType){
        subType.prototype = createObject(superType.prototype)
        Object.defineProperty(subType.prototype,"constructor",{
          enumerable:false,
          configurable:true,
          writable:true,
          value:subType
        })
      }
      // 父类
      function Person(name,age){
        this.name = name
        this.age = age
      }
      Person.prototype.eating = function(){
        console.log(this.name,"在吃饭");
      }
      //调用继承实现封装的方法
      inheritPrototype(Student,Person)
      // 子类
      function Student(name,age,sno){
        Person.call(this,name,age)
        this.sno = sno
      }
      Student.prototype.studying = function(){
        console.log(this.name,"在学习");
      }
      var p1 = new Student("张三",18,100)
      console.log('Student.prototype: ', Student.prototype);
      console.log('p1: ', p1);
      console.log('p1.__proto__: ', p1.__proto__);
      p1.eating()
      p1.studying()

补充:

查找对象是否有某个属性的方法

    console.log(obj.hasOwnProperty("name")); //false 不顺着原型找,只找当前对象 
    console.log("name" in obj); //true in操作符查找 会顺着原型链

instanceof判断数据类型的原理,判断构造函数的原型是否在某个实例的原型上

    [] instanceof Array // true   Array.prototype 在 [] 的原型上
    [] instanceof Object // true  Object.prototype 在 [] 的原型上
    a.isPrototypeOf(b) //a对象是否存在于b的原型链上