继承和原型链

继承

本来想先说说原型链,但是感觉先说一下继承会更好的理解原型链,那好,废话不多说,开始!!
这里的继承是面向对象里面的范畴;

继承就是让一个类型(可以通过构造函数或者class来定义)的对象能够访问另外一个类型的属性和方法,它是类和类之间的一种关系,通常说子类继承父类。但这里容易出现一个误区:认为实例继承了某个类,某人有响应的属性和方法是因为他继承人类,这种说法是错误。

注明:以下举例比较随意,看个人理解
比如,现在有一个"动物"构造函数

 function Animal(){
      this.type="动物";
    }

要求:怎么才能让"猫"继承"动物"呢?

  • 继承的第一种方法:
    使用call和apply方法,将父元素的构造函数绑定到子对象上,即在第一行加一个function函数名.call(this);
<script>
    //当Animal没有参数的情况
    function Animal(){
      this.type="动物";
    }
    //当Animal有参数的情况
     function Animal(type){
      this.type = type;
    }
    // 先创建一个Cat的构造函数,会被它的实例对象 cat1 继承
    function Cat(){
      // 加下面这句的话,cat1就可以访问Animal了
      Animal.call(this);
      Animal.apply(this); 
      // 当Animal里面有参数时:
      Animal.call(this,"动物");
      Animal.apply(this,["动物"]); 
      //apply和call区别:
      // 在Animal没有参数的情况下和上面call一样
      // 在Animal有参数的情况下,apply需要把参数放在一个数组里面
      this.color="blue"
    }
    var cat1=new Cat();
    console.log(cat1.color); //blue
    console.log(cat1.type); // 动物
    // 缺点:如果Animal里面有很多属性和方法,而cat1并不想全部要,这样实现起来就出现一些问题

  </script>

注意点:

apply和call区别:
在Animal没有参数的情况下,apply和上面call一样
在Animal有参数的情况下,apply需要把参数放在一个数组里面

  • 继承的第二种方法:
    使用prototype属性,如果“猫”的prototype对象,指向一个Animal的实例,那么所有“猫”的实例就能继承Animal;
    function Animal(type){
      this.type = type;
    }
    function Cat(){
      this.color = "blue";
    }
    Cat.prototype = new Animal("动物");
    // 注意此时,Cat已经把Animal的属性和方法继承过来
    // Cat.prototype.constructor = Cat;
    var cat1 = new Cat();
    console.log(cat1.type); //动物
    console.log(cat1.constructor); //在不手动纠正的情况下,cat1.constructor指向的是Animal
    // 问题:Cat 的实例对象 就是cat1,cat1.constructor不是指向的Cat,而是Animal,需要手动矫正一下属性
    Cat.prototype.constructor = Cat;
    console.log(cat1.constructor); //在手动矫正一下属性之后,在打印的话就是指向Cat

注意点:

将Cat的prototype对象指向一个Animal的实例,会删除原来的prototype的值;
prototype对象都有一个constructor属性,指向它的构造函数;
每一个实例也有一个constructor属性,默认调用prototype对象的constructor属性
最后注意:要重新去定义一下Cat的constructor属性

  • 继承的第三种方法:
    第三种方法是对第二种的改进,由于Animal对象中,不变的属性都可以写入Animal.prototype。所以,也可以让Cat()跳过Animal(),直接继承Animal.ptototype。
 function Animal(type){
      this.type = type;
    }
    Animal.prototype.say=function(){
      alert("hello");
    }
    function Cat(){
      this.color = "blue";
    }
    Cat.prototype = Animal.prototype;
    Cat.prototype.constructor = Cat;
    var cat1 = new Cat();
    cat1.say();
    console.log(cat1.type); //undefined
    // console.log(cat1.constructor); 
    
    // 创建一个 Animal 的实例对象 ani1
    var ani1=new Animal("动物");
    console.log(ani1.constructor);

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意点:

与前一种比较,这样做的优点是效率比较高(不用执行和建立Animal的实例了),比较省内存。
缺点:Cat.prototype和Animal.prototype现在指向同一个对象,那么任何对Cat.prototype的修改,都会反映到Animal.prototype。

  • 继承的第四种方法:
    利用一个空对象作为中介(常用方法)
 function Animal(type){
      this.type = type;
    }
    Animal.prototype.say=function(){
      alert("hello");
    }
    function Cat(){
      this.color = "blue";
    }
    // Cat.prototype = Animal.prototype;
    // Cat.prototype.constructor = Cat;
    var F=function(){};
    F.prototype=Animal.prototype;
    // F的原型对象里面的方法是animal的全部的方法
    // 让Cat的原型对象直接指向 F 的实例对象,会把F里面的方法拿过来
    Cat.prototype=new F();
    Cat.prototype.constructor = Cat;
    var cat1 = new Cat();
    cat1.say();
    console.log(cat1.constructor); 

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

注意点:

F是空对象,所以几乎不占内存。这时,修改了Cat的prototype对象,就不会影响Animal的prototype对象

  • 继承的第五种方法:
    拷贝继承
 function Animal(type){
      this.type = type;
    }
    Animal.prototype.say=function(){
      alert("hello");
    }
    function Cat(){
      this.color = "blue";
    }
    function extend(par,child) {
      var par_prototype=par.prototype;
      var child_prototype=child.prototype;
      for(x in par_prototype){
        child_prototype[x]=par_prototype[x];
      }
    }
    extend(Animal,Cat);
    var cat1 = new Cat();
    cat1.say();
    console.log(cat1.constructor); 

在这里插入图片描述
是什么原因导致通过改变prototype的指向能够完成这样一个继承呢?
所以下面说说原型链吧

原型链

1、普通函数和函数对象
在JavaScript中,一切皆对象,但是对象也有区别的,分为普通对象和函数对象,Function是js自带的函数对象

    function f1(){};
    var f2=function(){};
    var f3=new Function('str','console.log(str)');
    var o3=new f1();
    var o1={};
    var o2=new Object();
    console.log(typeof Object);//function
    console.log(typeof Function);//function
    console.log(typeof o1);//object
    console.log(typeof o2);//object
    console.log(typeof o3);//object
    console.log(typeof f1);//function
    console.log(typeof f2);//function
    console.log(typeof f3);//function

o1、o2、o3 为普通对象
f1、f2、f3 为函数对象
怎么区分呢?
凡是通过new function()创建的对象都是函数对象,其他的都是普通对象。
f1/f2/f3归根结底都是通过new Function()的方式进行创建的
Function Object 也都是通过new Function()创建的

在这里插入图片描述
在JavaScript中,每当定义一个对象(函数)的时候,对象中都会包含一些预定义的属性。其中函数对象的一个属性就是原型对象 prototype。注意:普通对象没有prototype,但是有 _ proto _ 属性 。

2、原型对象

原型对象其实就是普通对象(function、object除外,它是函数对象,但是它很特殊,它没有prototype属性(前面说到函数对象都有prototype属性))。看下面这个案例:

    function f1() {};
    console.log(f1.prototype);
    console.log(typeof f1.prototype);
    console.log(typeof Object.prototype);
    console.log(typeof Function.prototype.prototype);

在这里插入图片描述
从这句console.log(f1.prototype);的输出结果来看,f1.prototype就是f1的实例对象。就是在f1创建的时候,创建一个它的实例对象并赋值给它的prototype,基本过程如下:

    var temp=new f1();
    f1.prototype=temp;
    
    // 那么Function也就迎刃而解:
    var temp1=new Function();
    Function.prototype=temp1;

那原型对象是用来作甚的呢?主要是用于继承。举例说明:

    var person=function(name){
      this.name=name;
    }
    person.prototype.getName=function(){
      return this.name;
    }
    var veb=new person('fqniu');
    veb.getName();

从这个例子中可以看出,通过给person.prototype设置了一个函数对象的属性,那有person实例(例子中的:veb)出来的普通对象就继承了这个属性。具体怎么实现的继承,就要看下面的原型链了。

3、原型链
js在创建对象(不论是普通对象还是函数对象)的时候,都有一个叫做_proto_ 的内置属性,用于指向创建它的函数对象的原型对象prototype

    console.log(veb.__proto__ === person.prototype);//true
    
    // 同样,person.prototype对象也有_proto_属性,它指向创建它的函数对象(object)的prototype
    console.log(person.prototype.__proto__ === Object.prototype);//true
    
    // 继续,object.prototype对象也有_proto_属性,但它比较特殊,为null
    console.log(Object.prototype.__proto__);//null

在这里插入图片描述
然后把这个有_proto_ 串起来的直到Object.prototype. _ proto _ 为null的链叫做原型链。它会一直往上找
(_ proto _),知道为null为止,如下:

veb(_ proto _)——> person.prototype( _ proto _)——>Object.prototype( _ proto _)——>null

例如如下图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

那先说到这里,还需要进一步整理和消化才行!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值