zoukankan      html  css  js  c++  java
  • 继承初体验

    剧透:继承方式  call apply  原型继承 原型链继承 原型拷贝继承(完美) 混合继承(完美) 寄生继承(完美) 

    前戏什么的就省了,直接步入正题。

    继承是什么呢?

    js里面的一大特性,

    js是作为面向对象的弱类型语言,

    面向对象的三大核心部分是:

    抽象:抓住核心问题

    封装:不考虑内部实现,只考虑功能使用

    继承:从已有的对象上继承出新的对象,子级继承父级

    多态:js中不存在的 php java中的 难度?颗星

    寻根问源 找到继承之后

    如何实现继承呢?

    call apply

    先了解一下call apply 两者的区别:

    相同点:

    改变this的指向

    差异:

    call(,)两个参数:第一个是this的指向 第二个是需要传递的值

    apply(,)同上,但是第二个是传入一个数组

     举个栗子:

    var arr = [1,2,3]
    //数组去借Math对象里面的max方法 console.log(Math.max.apply([],arr))
    //3

     简单粗暴 上代码

     ①call apply  继承

    复制代码
    //先请出吴彦祖
    var person = {
        name:"吴彦祖",
    //吴彦祖有个抛媚眼的技能
    shuai:function(skill){
      console.log(this.name+"向你抛了个"+skill)  
    }      
    }
    person.shuai("媚眼")
    //吴彦祖向你抛了个媚眼
    
    //这个时候 黄渤来了
    var son ={
    name:"黄渤"
    }
    //黄渤也想向你抛媚眼
    //这个时候浏览器就看不下去了赶紧报了个错
    son.shuai()
    //not a function 不是一个方法
    
    //然后黄渤就去找吴彦祖拿这个技能
    //吴彦祖就很勉强的把这个撩妹的技能给了黄渤
    person.shuai.call(son,"抛了个媚眼")
    //这里的call()里面的两个参数:①this的指向②需要传递的值,值可以传递多个,每个之间用 逗号 , 隔开。
    //直接找到吴彦祖而不去使用他的技能 不传值 会报undefind
    person.shuai.call(son)//黄渤向你抛了个undefind
    person.shuai.call(son,"媚眼")//黄渤向你抛了个媚眼
    
    
    复制代码

    总结:call apply 是最糙的继承方式。多用于属性继承(子级继承父级),也可继承方法,但是需要写在函数内,而写在函数内的方法占用内存,损耗资源,通常会把方法写在原型上。故而call apply 继承不到。方法写在原型的好处就是优化性能 减少内存占用 提高用户体验。

    话不多说 上代码 接下来使用构造函数

    复制代码//人具有的通用属性 名字,年龄,还有吃这个技能等等
    function Person(name,age){
            this.name = name;
            this.age = age;
            this.eat = function(){} 
    }
    //具体到某个人的话 每个人有自己独特的Y染色体等
    function Son(y,name,age){
         //使用call继承上面的通用属性
    //this指向当前Son ,然后Son去借Person的哪些属性
    //通用属性传入之后需要给到Son的形参中 继承的属性语法要写在本身属性之前 否则本身会被覆盖
    Person.call(this,name,age) this.y = y
    } //实例化对象 //这里实例化new的过程中具体做了些什么操作呢? var wuyanzu = newSon(12,"吴彦祖","18") //此时的吴彦祖不仅有通用属性还有吃的这个技能

    复制代码

     前面说到方法需要写在原型上 下面进行一番改造

    复制代码
    function Person(name,age){
            this.name = name;
            this.age = age;
            //this.eat = function(){} 
    }
    Person.Prototype.eat = function(){代码块...}
    function Son(y,name,age){
        Person.call(this,name,age)
        this.y = y
    }
    var wuyanzu = newSon(12,"吴彦祖","18") 
    //此时的吴彦祖继承到了属性 但是没有继续到方法
    //那么问题来了 我们应该如何继承到写在原型上的方法呢?

    那么什么是原型呢?

    看完概念上代码:

    Prototype:每一个函数里面都有一个Prototype属性,这个属性叫做原型,这个原型指向一个对象,我们把这个对象叫做原型对象。

    原型对象里面有两个东西:

    ①:constructor:构造器 作用是指向创建自己的那个构造函数

    ②__proto__

    a.通过new实例化对象之后,里面都会有一个__proto__这个属性

    b.__proto__指向一个对象,这个对象是原型对象

    c.实例化对象可以直接访问__proto__里面的一些方法

    原型继承

    function Father(age,name){
        this.name = name;
        this.age= age;
    }
    
    Father.prototype.eat = function(){}
    Father.prototype.sleep=function(){}
    
    function Son(y,age,name){
        Father.call(this,age,name)
        this.y = y;
    }
    //重点来了
    Son.prototype = Father.prototype;
    Son.prototype.work = function(){}
    

     var wuyanzu = new Son(12,"吴彦祖",18)
     var w = new Father()
     console.log(wuyanzu)
     console.log(w)

    是的 细心的你肯定发现了什么。

    原型继承的缺点正是在此。

    父级被子级污染。(黄色箭头)

    那么有没有更好的继承方式呢?

    答案是有的。

     原型链继承

    首先说一下什么是原型链呢?链?链条?一环扣一环。这正是原型链的形式,在jQuery中我们会发现我们的代码会经常....点下去,这正是链式操作的强大。在原型当中,由__proto__组成的链条叫做原型链。实例化对象通过链条向父级,子级,借用方法,通俗点的说您通过DNA染色体和您的父亲建立了血缘关系。

    比如我们实例化了一个对象 W 然后W.__proto__.__proto__.toString

    W本身没有toString这个方法,但是实例化对象可以直接访问__proto__里面的一些方法,而__proto__指向一个对象,这个对象是原型对象,原型对象中的方法和属性可以被访问到。

    话不多说 上代码

    function Father(age,name){
        this.age=age;
    this.name=name; } Father.prototype.eat = function(){} Father.prototype.sleep = function(){} function Son(y,age,name){ Father.call(this,age,name) this.y=y } Son.prototype = new Father(); Son.prototype.work = function(){} var wuyanzu = new Son() var w = new Father() console.log(wuyanzu) console.log(w)


    原型链继承的特点:
    ①代码简易,实现简单

    ②子类能访问到父类原型新增的方法或属性

       ③实例是子类的实例,也是父类的实例,两者关联性强


    但是细心的你肯定又发现了
    ①实例化对象的__proto__应该指向构造函数 object 这里的却指向继承的Father
    ②少了constructor
    ③多了一些无用的属性

    那么有没有更完美的继承方式呢?
    答案依旧是有的。


    混合继承(完美)

    话不多说 上代码

    function Father(age,name){
        this.age=age;
        this.name=name
    }
    Father.prototype.eat = function(){}
    Father.prototype.sleep = function(){}
    
    function Son(y,age,name){
        Father.call(this,age,name)
        this.y=y
    }
    //划重点 少啥补啥
    Son.prototype ={
         constructor:Son,
         __proto__:Father.prototype (等于Son.prototype = new Father())
     }
    Son.prototype.work = function(){}
    
    var wuyanzu = new Son()
    var w = new Father()
    console.log(wuyanzu)
    console.log(w)

    怎么个完美呢?

    ①父级没有被污染

    ②指向正确

    推荐使用:5星

    再来几个

    寄生继承(完美)

    何为寄生?既生瑜何生亮?nonono

    寄生----虫

    通过一个寄生壳子 依附功能

    话不多说 上代码

    function Father(age,name){
        this.age=age;
        this.name=name
    }
    Father.prototype.eat = function(){}
    Father.prototype.sleep = function(){}
    
    function Son(y,age,name){
        Father.call(this,age,name)
        this.y=y
    }
    
    //寄生壳子
     function fn(){}
     fn.prototype = Father.prototype;
     Son.prototype = new fn();
     Son.prototype.constructor = Son
     Son.prototype.work = function(){}
    
    var wuyanzu = new Son()
    var w = new Father()
    console.log(wuyanzu)
    console.log(w)

     优点:

    混合继承会有两次调用的情况,寄生继承解决了这个问题。

    最后再上一个

    原型拷贝继承(完美)

    function Father(age,name){
        this.age=age;
        this.name=name
    }
    Father.prototype.eat = function(){}
    Father.prototype.sleep = function(){}
    
    function Son(y,age,name){
        Father.call(this,age,name)
        this.y=y
    }
    Son.extend = function(Father){
        for(var key in Father){
            this[key] = Father[key]
        }
    }
    Son.prototype.work = function(){}
    var wuyanzu = new Son()
    var w = new Father()
    console.log(wuyanzu)
    console.log(w)

    优点在于可以进行多继承

    缺点就是同时拷贝到了父类的属性,对于本身来说是负重的,增加了自身的体积,占用了内存资源,效率较低的。

    除此之外ES6也提供了继承方法诸如class super.....

    tips:

    instanceOf 检测是否是一个数组

    Object{[native code]}这是一个系统封装好的函数 无法查看

    null作为原型链的终点(object.prototype.__proto__   //null)

    点到即止 

    今天就到这吧。。。

    最后灵魂画手上手了。。。

    中间误删了草稿(感谢管理contact@cnblogs.com的恢复!)

  • 相关阅读:
    常用正则表达式
    java中的异或运算
    Eclipse 中,web项目在Tomcat运行时填写不了Server name
    错误: 找不到或无法加载主类 tomcat-juli.jar
    Hadoop学习之Ubuntu12.04 Hadoop 环境搭建笔记
    ubuntu安装 cober 笔记
    Ubuntu中安装JDK
    对象调用很容易忽视的地址问题
    2018.09.27_练习时的tips
    网页的组成(一)
  • 原文地址:https://www.cnblogs.com/522040-m/p/9052128.html
Copyright © 2011-2022 走看看