6.29 js笔记

原型链继承

构造函数、原型与实例之间的关系

  • 每创建一个函数,该函数就会自动带有一个 prototype 属性。该属性是个指针,指向了一个对象,我们称之为 原型对象。什么是指针?指针就好比学生的学号,原型对象则是那个学生。我们通过学号找到唯一的那个学生。假设突然,指针设置 null, 学号重置空了,不要慌,对象还存在,学生也没消失。只是不好找了。

  • 原型对象上默认有一个属性 constructor,该属性也是一个指针,指向其相关联的构造函数。

  • 通过调用构造函数产生的实例,都有一个内部属性,指向了原型对象。所以实例能够访问原型对象上的所有属性和方法。

  • 所以三者的关系是,每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。通俗点说就是,实例通过内部指针可以访问到原型对象,原型对象通过constructor指针,又可以找到构造函数。
下面看一个例子:


function Dog (name) {
    this.name = name;
    this.type = 'Dog'; 
}
Dog.prototype.speak = function () {
  alert('wang');
}
var doggie = new Dog('jiwawa');
doggie.speak();  //wang 
   

以上代码定义了一个构造函数 Dog(), Dog.prototype 指向的原型对象,其自带的属性construtor又指回了 Dog,即 Dog.prototype.constructor == Dog.实例doggie由于其内部指针指向了该原型对象,所以可以访问到 speak方法。

在这里插入图片描述

原型链
  前面我们说到,所有的实例有一个内部指针,指向它的原型对象,并且可以访问原型对象上的所有属性和方法。doggie实例指向了Dog的原型对象,可以访问Dog原型对象上的所有属性和方法;如果Dog原型对象变成了某一个类的实例 aaa,这个实例又会指向一个新的原型对象 AAA,那么 doggie 此时就能访问 aaa 的实例属性和 AA A原型对象上的所有属性和方法了。同理,新的原型对象AAA碰巧又是另外一个对象的实例bbb,这个实例bbb又会指向新的原型对象 BBB,那么doggie此时就能访问 bbb 的实例属性和 BBB 原型对象上的所有属性和方法了。

这就是JS通过原型链实现继承的方法了。看下面一个例子:

//定义一个 Animal 构造函数,作为 Dog 的父类
function Animal () {
    this.superType = 'Animal';
}

Animal.prototype.superSpeak = function () {
    alert(this.superType);
}

function Dog (name) {
    this.name = name;
    this.type = 'Dog';  
}
//改变Dog的prototype指针,指向一个 Animal 实例
Dog.prototype = new Animal();
//上面那行就相当于这么写
//var animal = new Animal();
//Dog.prototype = animal;

Dog.prototype.speak = function () {
  alert(this.type);
}
var doggie = new Dog('jiwawa');
doggie.superSpeak();  //Animal

以上代码,首先定义了一个 Animal 构造函数,通过new Animal()得到实例,会包含一个实例属性 superType 和一个原型属性 superSpeak。另外又定义了一个Dog构造函数。然后情况发生变化,代码中加粗那一行,将Dog的原型对象覆盖成了 animal 实例。当 doggie 去访问superSpeak属性时,js会先在doggie的实例属性中查找,发现找不到,然后,js就会去doggie 的原型对象上去找,doggie的原型对象已经被我们改成了一个animal实例,那就是去animal实例上去找。先找animal的实例属性,发现还是没有 superSpeack, 最后去 animal 的原型对象上去找才找到。
在这里插入图片描述

这就说明,我们可以通过原型链的方式,实现 Dog 继承 Animal 的所有属性和方法。

总结来说:就是当重写了Dog.prototype指向的原型对象后,实例的内部指针也发生了改变,指向了新的原型对象,然后就能实现类与类之间的继承了。(但是如果在重写原型对象之前,产生的实例,其内部指针指向的还是最初的原型对象。这个我下次再发篇文章讲。js 继承之借用构造函数继承)

共享属性迁移到原型

代码:

  <script>
   
        function Shape() {
            Shape.prototype.name='shape';
            Shape.prototype.toString=function () {
                return this.name;
            }
        }
        function TwoDShape() {}
            TwoDShape.prototype=new Shape();
            TwoDShape.prototype.constructor=TwoDShape;//重置
            TwoDShape.prototype.name='2D Shape';

        function Triangle(side,height){
            this.side=side;
            this.height=height;
        }//side和height不一定固定所以不适合放在原型中

       Triangle.prototype=new TwoDShape();
       Triangle.prototype.constructor=Triangle;
       Triangle.prototype.name='triangle';
       Triangle.prototype.getArea=function () {
           return this.side*this.height/2;
       }//因为得到面积的方式是固定的

       var myTriangle=new Triangle(5,10);
       console.log(myTriangle.getArea());
       console.log(myTriangle.toString());
       console.log(myTriangle.hasOwnProperty('side'));//检测自身属性
       console.log(myTriangle.hasOwnProperty('name'));
       console.log(myTriangle instanceof Triangle);
       console.log(myTriangle instanceof TwoDShape);
       console.log(myTriangle instanceof Shape);
    </script>

在这里插入图片描述

通过uber访问父类中的方法

 <script>
    function Shape() {};
    Shape.prototype.name='Shape';
    Shape.prototype.toString=function () {
        var result=[];
        if(this.constructor.uber){
            result[result.length]=this.constructor.uber.toString();//检查该属性是否有uber属性
        }
        result[result.length]=this.name;
        return result.join(',');
    };

    function TwoDShape() {};
        var F=function () {};//临时构造器
        F.prototype=Shape.prototype;
        TwoDShape.prototype=new F();
        TwoDShape.prototype.constructor=TwoDShape;
        TwoDShape.uber=Shape.prototype;//子对象对父对象的访问
        TwoDShape.prototype.name='2d';

        function Triangle(side,height) {
            this.side=side;
            this.height=height;
        }
        var F=function(){};
        F.prototype=TwoDShape.prototype;
        Triangle.prototype=new F();
        Triangle.prototype.constructor=Triangle;
        Triangle.uber=TwoDShape.prototype;//父级对象的一个原型引用
        Triangle.prototype.name='triangle';
        Triangle.prototype.getArea=function () {
            return this.side*this.height/2;
        }

        var my=new Triangle(5,10);
        console.log(my.getArea());
        console.log(my.toString());

        var td=new TwoDShape();
        console.log(td.toString());
    </script>

在这里插入图片描述

我们可以选择封装 减少代码重复

function extend(Child,Parent){
      var F=function () {};
          F.prototype=Parent.prototype;
          Child.prototype=new F();
          Child.prototype.constructor=Child;
          Child.uber=Parent.prototype;

  }//封装

  function Shape() {};
  Shape.prototype.name='Shape';
  Shape.prototype.toString=function () {
      var result=[];
      if(this.constructor.uber){
          result[result.length]=this.constructor.uber.toString();//检查该属性是否有uber属性
      }
      result[result.length]=this.name;
      return result.join(',');
  };

    function TwoDShape() {};
      extend(TwoDShape,Shape);
      TwoDShape.prototype.name='2d';
      
    function Triangle(side,height) {
          this.side=side;
          this.height=height;
      }
      extend(Triangle,TwoDShape);
    Triangle.prototype.name='Triangle';
    Triangle.prototype.getArea=function () {
        return this.side*this.height/2;
    }
      var my=new Triangle(5,10);
    console.log(my.getArea());

这样效果也是一样的

拷贝父类对象属性

  <script>
  function extend(Child,Parent){
      var F=function () {};
          F.prototype=Parent.prototype;
          Child.prototype=new F();
          Child.prototype.constructor=Child;
          Child.uber=Parent.prototype;

  }//封装继承

        function extend1(Child,Parent) {
            var p=Parent.prototype;
            var c=Child.prototype;
            for(var i in p ){
                c[i]=p[i];//父级拷贝给子级
            }
            c.uber=p;//子类中的uber指向父类原型
        }

        var Shape=function () {};
        var TwoShape=function () {};
        Shape.prototype.name='shape';
        Shape.prototype.toString=function () {
            return this.name;
        }
        extend(TwoShape,Shape);
        var td=new TwoShape();
        console.log(td.name);
        console.log(TwoShape.prototype.name);//继承的方式访问到
        console.log(td.__proto__.name);
        console.log(td.hasOwnProperty('name'));//是不是td对象的属性
        console.log(td.__proto__.hasOwnProperty('name'));//不是原型的属性
        extend1(TwoShape,Shape);
        var td= new TwoShape();
        console.log(td.__proto__.hasOwnProperty('name'));
        console.log(td.__proto__.hasOwnProperty('toString'));
        console.log(td.__proto__.toString==Shape.prototype.toString);

        //对象是被当作引用来使用的
        var A=function () {};
        var B=function () {};
        A.prototype.test=[1,2,3];
        A.prototype.test1='this is a test';
        extend(B,A);//拷贝
        console.log(B.prototype.hasOwnProperty('test'));
        console.log(B.prototype.hasOwnProperty('test1'));
        console.log(B.prototype.test);
        console.log(B.prototype.test===A.prototype.test);
        B.prototype.test1='hello';//不影响A
        console.log(A.prototype.test1);
        B.prototype.test.push(4,5,6);
        console.log(B.prototype.test);//会变
        console.log(A.prototype.test);//会变 数组里多了4 5 6

        //重写test属性
        B.prototype.test=['aa','bb','cc','dd'];//相当于将指针移动到新对象
        console.log(A.prototype.test);//A仍不变
        console.log(B.prototype.test);//B指针指向新对象


    </script>

结果:

在这里插入图片描述

深拷贝

    <script>
    var o={};
    function extendCopy(p) {
        var c={};
        for(var i in p){
            c[i]=p[i];
        }
        c.uber=p;
        return c;
    }//简单的拷贝

    var Shape={
        name:'shape',
        toString:function () {
            return this.name;
        }
    };

    var TwoDShape=extendCopy(Shape);
    TwoDShape.name='2D';//扩展
    TwoDShape.toString=function () {
        return this.uber.toString()+"--"+this.name;

    }//重写方法

    var Triangle=extendCopy(TwoDShape);//继承TwoDShape
    Triangle.name='Triangle';
    Triangle.getArea=function () {
        return this.side*this.height/2;
    }
    Triangle.side=5;
    Triangle.height=10;
    console.log(Triangle.getArea());
    
    //深拷贝可以等同于修改了函数
        
        function deepCopy(p,c){
            var c=c||{};
            for(var i in p){
                if(typeof p[i]==='object'){
                    c[i]=(p[i].constructor===Array)?[]:{};//判断p里的i是否为数组,是则以数组显示否则就是个对象赋给c里的i
                    deepCopy(p[i],c[i]);//然后递归调用
                }//如果是对象就进行深度拷贝
                else{
                    c[i]=p[i];
                }//否则将p的属性拷贝给c
            }
            return c;
        }

        var parent={
            numbers:[1,2,3]
        };
        var mydeep=deepCopy(parent);
        var myshallow=extendCopy(parent);
        mydeep.numbers.push(4,5,6);
        console.log(mydeep.numbers);//深拷贝
        console.log(parent.numbers);//深

        myshallow.numbers.push(7,8,9);
        console.log(myshallow.numbers);//浅拷贝
        console.log(parent.numbers);



在这里插入图片描述

多重继承

单继承

        function object(o) {
            function F() {};
                F.prototype=o;
                return new F();

        }//object函数接收父级对象


        //如果想访问uber属性
        function object(o) {
            function F() {};
            F.prototype=o;
            var n=new F();
            n.uber=o;
            return n;
        }//只需将某个对象传给它 就会创建一个新的对象


        var Shape={
            name:'shape',
            toString:function () {
                return this.name;
            }
        };
        var TwoDShape=object(Shape);
        TwoDShape.name='2D shape';
        TwoDShape.toString=function () {
            return this.uber.toString()+'---'+this.name;
        }
        var Triangle=object(TwoDShape);
        Triangle.name='triangle';
        Triangle.getArea=function () {
            return this.side*this.height/2;
        }
        console.log(Triangle.toString());//相当于原型的继承


在这里插入图片描述

单继承:

        function objectPlus(o,stuff){
            function F() {}
                F.prototype=o;
                var n=new F();
                n.uber=o;
                for(var i in stuff){
                    n[i]=stuff[i];
                }

            return n;
        }//o用于继承 stuff用于拷贝属性和方法

        var Shape={
            name:'shape',
            toString: function () {
                return this.name;
            }
        };

        var TwoDShape=objectPlus(Shape,{
            name:'2d',
            toString:function () {
                return this.uber.toString()+'---'+this.name;
            }
        });
        var Triangle=objectPlus(TwoDShape,{
            name:'triangle',
            getArea:function () {
                return this.side*this.height/2;
            },
            side:0,
            height:0
        });
        var my=objectPlus(Triangle,{
            side:5,
            height:10
        });
        console.log(my.getArea());
        console.log(my.toString());//Triangle出现2次 它的name属性被重复执行2次
        //为Triangle赋一个新的name属性就不会出现2次

        var my=objectPlus(Triangle,{
            name:'this is ',
            side:10,
            height:20
        });
        console.log(my.toString());

在这里插入图片描述

多继承:可以说是 子对象不止有一个父对象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值