zoukankan      html  css  js  c++  java
  • 面向对象之继承-5种JavaScript继承的方法


    今天我们讨论一下常用的几种继承方法:

    首先我们创建一个动物函数Animal:   function Animal () {     this.species = '动物'   }
    再写准备名叫猫咪的函数Cat:
       function Cat (name, color) {
        this.name = name
        this.color = color
      }
    最终目的是让Cat继承Animal的属性{spacies : "动物"}; 方法如下:
     
    一、 构造函数绑定

    首先解释下apply方法:
    官方定义: apply方法能劫持另外一个对象的方法,继承另外一个对象的属性.其中第一个参数是用来代替原函数的this,第二个参数是一个数组集合; 
    说人话就是在CatA里调用Animal方法,同时改变Animal方法的this指向:
    Animal.apply(this, arguments)
      function Animal () {
        this.species = '动物'
      }
    
      function CatA (name, color) {
        Animal.apply(this, arguments)
        this.name = name
        this.color = color
      }
    
      // 打印结果如下:
      console.log(new CatA('mimiA', 'red')) // CatA {species: "动物", name: "mimiA", color: "red"}
    apply方法是最简单也是最最基础的一种'继承'方式;
     
    二、prototype模式-new Animal()赋值给Cat

    父函数实例一下赋值给子函数:
    CatB.prototype = new Animal();
    这样写有一个小坑, 就是子函数的构造函数会被父函数覆盖, 指向了父函数; 解决方法有两个, 一个是重新修正指向, 另一个是拷贝赋值;
     function Animal () {
        this.species = '动物'
    }
    
    function CatB (name, color) {
        this.name = name
        this.color = color
    }
     
    CatB.prototype = new Animal()
    // 修正子函数的构造函数指向; 即: constructor: ƒ Animal -> constructor: ƒ CatB
    CatB.prototype.constructor = CatB
    // 打印结果如下: console.log(new CatB('mimiB', 'red')) // CatB {name: "mimiB", color: "red"} && {CatB.prototype.species: "动物"}
    说到原型继承, 为什么我们要这么做呢, 举个简单的栗子:
    我们写两个猫咪的函数, 如果不继承'species'属性, 我们就需要写两遍, 这样会出现冗余
    function CatAA (name, color) {
      this.name = name
    this.color = color
      this.species = '动物' // 公共属性
    }
    function CatBB (name, color) {
      this.name = name
    this.color = color
      this.species = '动物' // 公共属性
    }
    这个就像是标签里的class和style一样, 如果同一个样式多次出现, 我们就该考虑把他们提取出来, 放在class里; 
    关于构造函数和拷贝赋值, 我会在接下来两期单做说明 ( Link ), 在此不具;

    三、 prototype模式-Animal原型赋值给Cat原型

    原型链之间赋值:
    CatC.prototype = Animal.prototype
    这样改变原型属性,CatC原型的构造函数同样也会被Animal覆盖掉!
    需要修正Cat构造函数指向:
    CatC.prototype.constructor = CatC
    
    
    function Animal () {}
    Animal.prototype.species = "动物"
    
    function CatC (name, color) {
      this.name = name
      this.color = color
    }
    CatC.prototype
    = Animal.prototype CatC.prototype.constructor = CatC // 修正子函数的构造函数指向! // 注意此时出现一个隐患: 由于CatC和Animal的原型引用地址相同, 此时Animal原型的构造函数也被CatC污染了
    console.log(new CatC('mimiC', 'red')) // CatC {name: 'miniC', color: 'red'} && {CatC.prototype.species: "动物"} console.log(new Animal()) // Animal.prototype.constructor: CatC

        

    这里我们对上面的方法进行优化: 利用空对象作为中介,避免改变父级构造函数

    他其实是方法二和方法三的组合:
    首先写一个用来过渡的空函数F
    function F () {}
    先把Animal原型赋值给F, 因为F是一个无用的且几乎不占内存的空函数
    所以无须担心构造指向改变等问题
    F.prototype = Animal.prototype
    然后让Cat继承new F()
    CatD.prototype = new F()

    function Animal () {}
    Animal.prototype.species = "动物"
    
    function CatD (name, color) {
        this.name = name
        this.color = color
    }
    
    function F () {} // 写一个用来过渡的空函数F
    F.prototype = Animal.prototype
    
    CatD.prototype = new F() // 此时的F角色是: 具有Animal原型的空函数
    CatD.prototype.constructor = CatD // 修正子函数构造函数指向
    
    console.log(new CatD('mimiD', 'red')) //CatD.prototype.prototype.species: "动物"
    
    

    四、 拷贝继承

    我们可以开一个for in循环对原型上的属性进行拷贝赋值
    其中原型上的constructor是私有属性, 不会被遍历到的

    function Animal () {}
    Animal.prototype.species = "动物"
    
    function CatE (name, color) {
        this.name = name
        this.color = color
    }
    
    // 写一个函数原型属性拷贝的方法:
    function extend (src, fn) {
        var s = src.prototype, f = fn.prototype
        for (var i in f) {
            s[i] = f[i] // 其中原型上的constructor是私有属性, 不会被遍历到的
        }
    }
    extend(CatE, Animal)
    
    console.log(new CatE('mimiE', 'red')) // CatE {name: 'miniE', color: 'red'} && CatE.prototype.species: '动物'    
    
    

    五、 原型继承之ES6写法
    ;(function(){ // 为区分以上方法, 我把ES6写在了闭包里; 啰嗦一句,自调函数前面最好写一个分号,这样会降低多人协助报错率
    
        class Animal { // 定义了一个Animal的类,里面的构造函数必须写
            constructor () {
                this.species = '动物'
            }
        }
    
        class Cat extends Animal { // 定义了一个Cat类, 并且基层了上面的类Animal
            constructor (name, color) {
                super() // 继承类时, 需要super调用Animal
                this.name = name
                this.color = color
            }
        }
    
        console.log(new Cat('mimi','red')) // Cat {name: 'mini', color: 'red', species: '动物'} && Cat原型的构造函数是: class Cat;
    
    })()                        
    
    


    生活不易,请继续努力,在未来的路上,愿你步伐坚定且内心温柔。——ziChin
  • 相关阅读:
    在酷热的就业天气寻找几丝凉意
    《Orange ’ s :一个操作系统的实现 》作者自序
    Windows的设备驱动框架
    Windows的设备驱动框架中的上层与下层模块
    如何搭建自己的开发环境
    数据库设计指南(二)设计表和字段
    软件敏捷架构师
    Struts到JSF/Tapestry
    敏捷开发
    barcode制作条形码及破解
  • 原文地址:https://www.cnblogs.com/ziChin/p/10040139.html
Copyright © 2011-2022 走看看