• 继承


    1.继承

    继承分为:接口继承,实现继承,在js中只要实现继承,实现继承主要依靠原型链来做到的。

    定义:利用原型链让一个对象拥有另一个对象的属性和方法。

    让一个构造函数的原型对象等于另一个构造函数的实例

    function
    Fn1(){ this.num=100; } Fn1.prototype.getNum=function(){ console.log(this.num) }; var fn1=new Fn1();
    function Fn2(){}; Fn2.prototype=new Fn1(); var fn2=new Fn2();
    console.log(fn2.getNum())
    //100 console.log(fn2.__proto__) //Fn1 {num: 100} console.log(fn1.__proto__) //{getNum: ƒ, constructor: ƒ} console.log(Fn1.prototype) //{getNum: ƒ, constructor: ƒ}
    console.log(Fn1.prototype==fn1.__proto__) //true
    console.log(Fn2.prototype.__proto__==fn1.__proto__) //true console.log(fn1.__proto__==Fn1.prototype) //true
    console.log(Fn2.prototype.__proto__==Fn1.prototype) //true
    1.每个构造函数都有一个原型对象(prototype) 2.每个原型对象都包含一个指向该构造函数的constructor属性 3.每个new出来的实例都包含一个指向原型对象的隐式原型(__proto__)属性

    2.构造函数的进阶应用

    •  为什么我们要在构造函数里面定义属性,在构造函数的原型对象上写方法。
    1. 从实例化的过程来看,每个实例化对象的属性大多情况下都是不同的,所有大多数属性是可变的,定义在构造函数里面的属性优先级较高,所以实例化对象可以快速访问使用构造函数中的属性。
    2. 可以通过实例对象来访问保存在构造函数原型对象上的值,但是不能通过实例对象来重写原型上的值,如果一个实例对象修改了一个原型上的属性,其他实例对象在去访问这个实例属性的时候,这个实例属性就不是以前的实例属性了。
    3. 方法大多数情况下都是固定的,因此不必每次实例化的时候都把所有的方法实例化复制一遍。
    4. 构造函数的继承是依托于原型链来完成的,而在原型链中,构造函数的原型对象大多数情况下都是用来共享方法的,当然也可以共享一些衡邵改变的共享共用属性。
    5. 每次实例化都可以得到一个构造函数实例属性的副本,同时又可以共享着共用的方法,这样可以最大限度的节省内存。同时构造函数中的属性也支持床底参数来动态修改
    function Fn1(name){
        this.name=name;
        //this.getName=function(){}   //可以这样写--但不建议--原因3
    }
    //Fn1.prototype.name='1111';  //可以这样写--但不建议--原因2
    Fn1.prototype.getName=function(){
        console.log(this.name)
    }
    
    var fn1=new Fn1('2222').getName();  //2222
    var fn2=new Fn1('3333').getName();  //3333

    构造函数与原型组合使用的方式,在js组件化开发过程中是最通用用到的组件化开发方式

    3.继承的方式

    •  new实例化的过程
    1. 创建一个空对象,
    2. 把实例对象的隐式原型__proto__指向构造函数的原型prototype(实例对象拥有了构造函数prototype原型对象上的属性和方法)
    3. 把构造函数的this指向替换成实例对象,再执行构造函数,(实例对象拥有了构造函数的属性和方法)
    • 继承的方式
    1. 借用构造函数

          原理:子类型构造函数的内部调用父类型构造函数

        优点:1.子类可以继承父类构造函数中的属性  2.可以给父类构造函数中传递参数

        缺点:子类没办法继承父类构造函数原型上的方法

    function Fn1(name){
        this.name=name;
        this.num=100;
    }
    Fn1.prototype.getName=function(){
        return this.name
    }
    function Fn2(name){
        Fn1.call(this,name);
        this.job='work'
    }
    
    var fn2=new Fn2('name');
    console.log(fn2)   //Fn2 {name: "name", num: 100, job: "work"}

       2.组合继承

        原理:利用原型链实现对原型属性和方法的继承,同时通过借用构造函数来实现子类对父类构造函数内部属性的继承(先用借用构造函数的形式让子类继承父类构造函数的属性,再用原型链让子类继承父类原型对象的方法)

        优点:子类可以继承父类构造函数原型上的方法

        缺点:1.会调用2次父类构造函数(一次构建子类原型对象,一次子类构造函数内部构建子类属性)

    function Fn1(name){
        this.name=name;
        this.num=100
    }
    Fn1.prototype.getName=function(){
       console.log(this.name)
    }
    function Fn2(name){
        Fn1.call(this,name)    //借用构造函数继承属性
        this.job='work'
    }
    Fn2.prototype=new Fn1();   //使用原型链继承方法
    Fn2.prototype.constructor=Fn2;   //非必写--为了修正子类原型对象的constructor的属性
    Fn2.prototype.getName=function(){
      console.log(this.name+'-22')
    } var fn2=new Fn2('name2'); fn2.getName(); //name2-22 console.log(fn2) //Fn2 {name: "name2", num: 100, job: "work"}

    //console.log(fn2 instanceof Fn2) //true
    //console.log(fn2 instanceof Fn1) //true
    //console.log(fn2 instanceof Array) //false
    //console.log(fn2 instanceof Object) //true

    //console.log(Fn2.prototype.isPrototypeOf(fn2)) //true
    //console.log(Fn1.prototype.isPrototypeOf(fn2)) //true
    //console.log(Array.prototype.isPrototypeOf(fn2)) //false
    //console.log(Object.prototype.isPrototypeOf(fn2)) //true
    //console.log(Object.isPrototypeOf(fn2)) //false

      3.原型式继承(浅拷贝)

        场景:让一个对象与另一个对象保持类似结构。    

        原理:借助原型可以基于已有的对象创建新对象,同时还不必因此创建自定义类型

        优点:构造函数内部和原型上的属性和方法都可以继承

        缺点:包含引用类型值的属性会共享相应的引用类型的值。

    var obj1={
        name:'name1',
        num:[100,200],
    }
    //var obj2=obj1;
    //console.log(obj2)   //{name: "name1", num: 100}
    
    var obj2=Object.create(obj1);
    obj2.name='name2';
    obj2.num.push(300)
    //console.log(obj2.name);  //name2
    //console.log(obj2.num);   //[100, 200, 300]
    //console.log(obj1.name); //name2
    //console.log(obj1.num);   //[100, 200, 300]
    
    var obj3=Object.create(obj1);
    obj3.name='name3';
    obj3.num.push(400)
    console.log(obj3.name);   //name3
    console.log(obj3.num);   //[100, 200, 300, 400]
    console.log(obj2.name);  //name2
    console.log(obj2.num);  //[100, 200, 300, 400]
    console.log(obj1.name);   //name1
    console.log(obj1.num);   //[100, 200, 300, 400]

      4.寄生式继承

        原理:定义一个独立的函数,在这个函数内部以某种方式来增强参数对象,在增加对象之后再返回对象

    var obj1={
        name:'name1',
        num:100,
    }
    function hanceObj(obj){
        var newObj=Object.create(obj);
        newObj.setObj=function(){
            console.log('23234')
        }
        return newObj;
    }
    var obj2=hanceObj(obj1);
    console.log(obj2) 
     //{setObj: ƒ} { __proto__:{name: "name1" num: 100}}

      5.寄生组合继承

        原理:让第三方构造函数来寄生父类的原型,在子类构造函数内部使用父类.call(子类的实例)改变父类的构造函数的this指向

        优点:1.解决组合继承调用2次父类构造函数的问题  2.节约内存开销

    function Fn1(name){
        this.name=name
    }
    Fn1.prototype.getName=function(){
        console.log(this.name)
    }
    
    function Fn2(name){
        Fn1.call(this,name);
    }
    
    hanceObj(Fn2,Fn1);
    
    function hanceObj(f2,f1){
        var prototype=Object.create(f1.prototype);
        prototype.constructor=f2
        f2.prototype=prototype;
    }
    
    var fn2=new Fn2('name222');
    fn2.getName()    //name222
  • 相关阅读:
    临时文档
    栈与堆的区别及其探讨
    经典面试题(一)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯
    牛顿迭代法求根
    背包问题---01背包|完全背包(装满背包的方案总数分析及实现)
    背包问题----完全背包(最优方案总数分析及实现)
    背包问题----完全背包(详解|代码实现|背包具体物品的求解)
    60行代码:Javascript 写的俄罗斯方块游戏
    同学们博客链接汇总
    (2015秋) 作业1:学生调研(总分10分)
  • 原文地址:https://www.cnblogs.com/susu2020/p/16372339.html
Copyright © 2020-2023  润新知