美文网首页前端开发
深入理解JS面向对象 - JavaScript实现继承的五种方式

深入理解JS面向对象 - JavaScript实现继承的五种方式

作者: 番茄沙司a | 来源:发表于2018-09-05 18:52 被阅读73次

    一、类与实例

    1. 类的声明

    1. 传统function类的声明
    function Animal() {
      this.name = 'name';
    }
    
    1. ES6中的class声明
    class Animal2 {
      //构造函数
      constructor (){
        this.name = name;
      }
    }
    
    1.1 ES6 class与ES5 function的区别:

    Class的特点:
    Class在语法上更加贴合面向对象的写法
    Class实现继承更加易读,更加容易理解
    更易于写Java等后端语言的使用
    本质还是是语法糖,使用prototype

    1. 重复定义
    • function会覆盖之前定义的方法
    • class会报错
    1. 构造器 constructor
    • 在function定义的构造函数中,其prototype.constructor属性指向构造器自身
    • 在class定义的类中,constructor也相当于定义在prototype属性上
    1. 原型或者类中方法的枚举
    • class中定义的方法不可用Object.keys(Point.prototype)枚举到
    • function构造器原型方法可被Object.keys(Point.prototype)枚举到,除了constructor
    • 所有原型方法属性都可用Object.getOwnPropertyNames(Point.prototype)访问到
    • 不管是class还是functionconstructor属性默认不可枚举

    2. 生成实例

    如何通过类实例类的对象

      console.log(new Animal(), new Animal2());
    

    注:如果构造函数没有声明的话,new函数名后面的括号可以省略

    二、类与继承

    2.1 如何实现继承

    2.2 继承的几种方式

    1. 借助构造函数实现继承
      原理:在子类的函数体内执行父类,用call和apply都可以改变函数运行的上下文,导致父类执行的实例都会挂载到子类上面。
    function Parent1 () {
      this.name = 'parent1';
    }
    Parent1.prototype.say = function () {};/*new Child1().say() 报错*/
    function Child1 () {
      Parent1.call(this);/*apply也可以改变函数运行的上下文*/
      this.type = 'child1';
    }
    

    控制台输出:

    console.log(new Child1);
    

    缺点:构造函数除了函数体里面的内容,还可能有原型链上的,但是这种方式中构造函数原型链上的东西并没有被继承
    扩展学习:javascript中apply、call和bind方法的区别

    1. 借助原型链实现继承(弥补构造函数继承不足)
    function Parent2() {
        this.name = 'parent2';
        this.play = [1, 2, 3];
    }
    
    function Child2() {
        this.type = 'child2';
    }
    Child2.prototype = new Parent2();
    //缺点
    console.log(new Child2());
    var o1 = new Child2();
    var o2 = new Child2();
    console.log(o1.play, o2.play);//都为1,2,3
    
    o1.play.push(4);//将o1重新复制
    
    console.log(o1.play, o2.play);//都为1,2,3,4
    
    

    实现原理:低级构造函数的原型是高级构造函数的实例,new Child2().__proto__就是Parent2父类的一个实例对象。即new Child2().__proto__ === Parent2.prototypetruenew Child2().__proto__.name的值为parent2
    缺点:因为原型链中的原型是共用的,所以两个对象不隔离。改变一个影响另一个。o1.__proto__ = o2.__proto__

    1. 组合继承方式(构造函数+原型链)
    function Parent3() {
        this.name = 'Parent3';
        this.play = [1, 2, 3];
    }
    
    function Child3() {
        Parent3.call(this);
        this.type = 'Child3';
    }
    Child3.prototype = new Parent3();
    //检验
    console.log(new Child3());
    var o1 = new Child3();
    var o2 = new Child3();
    
    o1.play.push(4);
    console.log(o1.play, o2.play);//这时候结果就不一样了 分别是 [1,2,3,4] [1,2,3]
    

    缺点:实例化子类的时候父类构造函数执行两次。是没有必要的。

    1. 组合继承优化一
    function Parent4() {
        this.name = 'Parent4';
        this.play = [1, 2, 3];
    }
    
    function Child4() {
        Parent4.call(this);
        this.type = 'Child4';
    }
    Child4.prototype = Parent4.prototype;
    console.log(new Child4());
    var o1 = new Child4();
    var o2 = new Child4();
    
    o1.play.push(4);
    console.log(o1.play, o2.play);
    console.log(o1 instanceof Child4, s5 instanceof Parent4);//true, true
    console.log(o1.constructor);
    

    怎么区分,是否是直接实例化的?
    直接拿的父类实例

    1. 组合继承优化二
    function Parent5() {
        this.name = 'Parent5';
        this.play = [1, 2, 3];
    }
    
    function Child5() {
        Parent5.call(this);
        this.type = 'Child5';
    }
    Child5.prototype = Object.create(Parent5.prototype);
    

    创建中间对象,完美~

    2.3 Class和普通构造函数实现继承对比

    Class实现(ES6)
    class Animal {
        constructor(name) {
            this.name = name
        }
        eat() {
            console.log(`${this.name} eat`)
        }
    }
    
    class Dog extends Animal {
        constructor(name) {
            super(name)
            this.name = name
        }
        say() {
            console.log(`${this.name} say`)
        }
    }
    const dog = new Dog('husky')
    dog.say()
    dog.eat()
    
    普通构造函数实现
    function Animal() {
        this.eat = function () {
            alert('Animal eat')
        }
    
    }
    
    function Dog() {
        this.bark = function () {
            alert('Dog bark')
        }
    }
    
    Dog.prototype = new Animal()
    var husky = new Dog()
    
    husky.bark()
    husky.eat()
    

    相关文章

      网友评论

        本文标题:深入理解JS面向对象 - JavaScript实现继承的五种方式

        本文链接:https://www.haomeiwen.com/subject/vuzowftx.html