10-原型+原型链与继承

原型+原型链与继承

首先明确一点,一般说的对象,指的是已经实例化以后的;

  • console.dir

功能作用:显示一个对象所有的属性和方法 可以看出原型链关系

使用普通构造函数生成的实例对象构造函数console.dir以后;

结果说明:
构造函数的完整结构

console.dir(Person)的结果 构造函数内容 + prototype+__proto__(函数自己的原型Function.prototype

实例对象的完整结构
console.dir(per)的结果是 其构造函数定义的属性和方法(是赋值之后的)+__proto__(里面有一个constructor__proto__) ------------为什么是这样的结果,后面把原型链讲完就明了

​ 总结:

​ > 实例对象dir后 会显示 在构造函数中定义的属性和方法(是赋值之后的)

​ > 构造函数内容中 并没有这些属性和方法 (没有 age和name 等)

​ > 函数都有 prototype属性, 对象都有__proto__属性

​ 另外:如果按照实际结构:

		本来是应该是 per.__proto__.constructor==Person;
		但是规定      per.constructor==Person;  
        所以       console.log(per.constructor==Person)  //true
        			(虽然per并没有这个constructor属性的,但是有规定)

per.__proto__.constructor==Personinstanceof 都是可以检测 引用类型的方法

  • 通过原型解释实例对象和 构造函数的结构

    • 构造函数可以实例化对象

    • 构造函数中有一个属性叫prototype, 是构造函数的原型对象(把这个看成一个整体
      构造函数的原型

      					 **这部分就叫原型**
      
    • 构造函数的原型对象(prototype)中,也就是原型,它有两个属性:constructor__proto__

      constructor指向的就是 该原型对象所在的构造函数

      __proto__ 指向自己的原型对象 (这个时候这个原型 看成是另一个更高级原型的实例对象

      ​ (可以看出 原型和 构造函数 是相互指向的,也比实例对象 的级别高些

    • 实例对象的原型对象(proto)指向的是该构造函数的原型对象
      实例对象的原型

      实例对象的原型(__proto__)和 构造函数的原型 (prototype) 内部结构一模一样

      即: per.__proto__===Person.prototype

      ​ 可以感受到 实例对象的级别低一级

    • 通过构造函数的原型对象(prototype)中的方法是可以被实例对象直接访问的

      通过构造函数原型 添加的方法 (就是通过原型添加的方法)和直接在构造函数添加的方法在console.dir() 的时候 所处的位置是不一样的:

      通过原型添加的方法 只能在 原型对象中能找到,直接在实例对象中是没有的-------确实也应该是这样

    • 原型中的方法也是可以相互访问的:(在调用eat()中调用play) 实例对象中的方法,是可以相互调用的

    • 实例对象使用的属性或者方法, 先在实例中查找, 找到了则直接使用,找不到则去实例对象的原型__proto__中去找。

    • 原型的指向是可以改变的

      实例对象的原型__proto__指向的是该对象所在的构造函数的原型对象, 当构造函数的原型对象(prototype)指向如果改变了,实例对象的原型(__proto__)指向也会发生改变

      Student.prototype=new Person(10);-------改变student的原型

      ​ 这时候Student的原型 就是Person的实例对象;可以观察从继承看原型链图好好观察

      在原有的原型链上增加了一环

      指向改变后 ,通过Student实例化后的对象 也可以使用Person中的方法,(因为它是原型)

      如果想添加新的方法 必须在改变指向后 添加新的方法到原型;因为 改变指向是

      Student.prototype=new Person(10) 这种直接改变对象的指向添加的

      ​ 所以 之前添加的会被覆盖。

    实例对象、原型以及构造函数三者之间的关系:
    三者之间的关系

  • 原型的应用:

    • 数据共享 —节约资源

      • 将需要共享的数据放到原型中,不需要共享的数据放到 构造函数中

        直接 Person.prototype.newMethod 添加, 因为通过原型添加的方法和属性,实例对象也是可以访问到的。

    • 继承:后面介绍

  • 原型链及原型链的最终指向:

    一个原型对象 也是有原型的(这时候 可以看成原型对象是一个 实例对象),
    在这里插入图片描述

    举例:new Person() 是一个实例对象,其原型对象 是Person.prototype ,(person.__proto__ 指向 同Person.prototype)原型对象 也是一个对象 (可以认为它是 new Object 出来的实例对象  所以) 它的原型是 Object.prototype
        
    所以结构就是:
        		per----》Person.prototype----》Object.prototype----》null
    
    			(下一级原型相对 上一级 就像是一个实例对象 )
    
    

继承

  • 为什么要使用继承

    继承是为了解决冗余代码(两个对象有很多共同的属性方法,但又有不同的,这时候只用构造函数就不可以做到了,但是每一个都写一个构造函数,又会造成冗余,这时候可以采取继承,减少代码编写


    面向对象编程思想:根据需求,分析对象,找到对象有什么特征和行为,通过代码的方式来实现需求,要想实现这个需求,就要创建对象,要想创建对象,就应该显示有构造函数,然后通过构造函数来创建对象. 通过对象调用属性和方法来实现相应的功能及需求,即可---------------抽象出一个构造函数出来就相当于是在面对对象编程了。

    对于JS而言,它不是一门面向对象的语言, 而是一门基于对象的语言,那么为什么学习js还要学习面向对象,因为面向对象的思想适合于人的想法,编程起来会更加的方便, 及后期的维护

    面向对象的编程语言中有类(class)的概念(也是一种特殊的数据类型),但是JS不是面向对象的语言,所
    以,原生JS中没有类(class),但是可以模拟面向对象的思想编程, 会通过构造函数来模拟类的概念(class)

  • js继承实现的几种方式

    因为继承是必须要得到 父级中的所有属性方法(所以apply和原型在继承中必不可少使用)

    • 借用构造函数继承+apply

      缺点:父类原型上的东西是没法继承的

      function Person(name, age) {
            this.name = name;
            this.age = age;
            this.arr=[1,2,3,4];
            this.go=function () {
                console.log("gogogog")
            }
          }
      Person.prototype.sayHi = function () {
            console.log("您好");
        };
      
      function Student(name,age,score) {
            //借用构造函数
            Person.call(this,name,age);
            this.score = score;
       }
       
       var stu1 = new Student("小明",10,"100");
       console.log(stu1.name, stu1.age, stu1.score);
       stu1.sayHi()------------------报错
      
    • 借用原型链继承

      function Person(name, age) {
            this.name = name;
            this.age = age;
            this.arr=[1,2,3,4];
            this.go=function () {
                console.log("gogogog")
            }
          }
      Person.prototype.sayHi = function () {
            console.log("您好");
        };
      
      function Student(score) {
            this.score = score;
      }
      //通过改变原型获取父类的所有属性和方法 
      Student.prototype=new Person("xiaoxin",10)
      
      

      这样确实可以获取所有父级的方法属性(包括原型中的)

      但是也有缺点

      ​ 第一点: 因为 改变指向的时候Student.prototype=new Person("xiaoxin",10)

      ​ 所以通过new Student出来的子类name值和 age值初始值是固定好的;

      不能灵活的自己定义自己的初始属性值。

      第二点:因为改变指向 用的 是 Student.prototype=new Person("xiaoxin",10)

      ​ 这个 每一个 Student实例的原型 ,上级,也就是“根” 的存在;

      ​ 当在利用new Student 出多个实例的时候,其中一个更改student 原型(new Person实例)中 的是引用类型的属性,每个实例都随之改变。

      这块有点绕,以后慢慢理解

      var stu1=new Student(100);
      console.log(stu1.name+"=="+stu1.age+"=="+stu1.arr+"=="+stu1.score)
      
      var stu2=new Student(80);
      console.log(stu2.name+"=="+stu2.age+"=="+stu1.arr+"=="+stu2.score)
      
      stu1.name="keruisiya";
      console.log(stu1.name+"=="+stu1.age+"=="+stu1.arr+"=="+stu1.score)
      console.log(stu2.name+"=="+stu2.age+"=="+stu1.arr+"=="+stu2.score)
      stu1.arr[2]=10;
      console.log(stu1.name+"=="+stu1.age+"=="+stu1.arr+"=="+stu1.score)  //arr同时改变
      console.log(stu2.name+"=="+stu2.age+"=="+stu1.arr+"=="+stu2.score)
      

    • 组合继承(结合上序两种方法)

         function Person(name,age){
              this.name=name;
              this.age=age;
              this.getage=function () {
                  console.log(this.age);
              }
          }
      
          Person.prototype.sayHi=function () {
              console.log("hahaha");
          }
      
          function Student(name,age,score){
          	//借用构造函数:属性值重复的问题
              Person.call(this,name,age);
              this.score=score;
          }
      	//改变原型指向----继承	
          Student.prototype=new Person();//不传值(用来改变 指向的 )
      

      也还是有缺点的,因为 Person被执行了两次 ---------先就这样掌握就好

    • 组合继承优化

      参考网址: http://www.imooc.com/article/20162

    • ES6中继承 -----具体细节 等到es6中再提;

      语法:   *// 父类* 
      		class Father{    } 
      		*// 子类继承父类*  
      		class  Son extends Father {   }  
      		
      举例: class Parent {
      			constructor(name,age) {
                      this.name=name;
                      this.age=age;
                  }
                  say(){
                      console.log('我是你的爸爸!');
                  }
      		}
      	  class Child1 extends Parent {
              constructor(name, age, colors) {
                  super(name, age); // 调用父类的constructor(name, age)
                  this.colors = colors;
          	}
              toString() {
                 return this.colors + ' ' + super.say(); // 调用父类的say()
          	}
      	  }
      
      
  • 从原型链看继承

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值