javascript进阶系列(七)—— 继承

112 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

前言

大家好呀,我是L同学。在上篇文章javascript进阶系列(六)—— 原型中,我们学习了prototype-原型属性、__proto__ 原型属性、this指向等相关知识点。今天,在这篇文章中,我们继续学习继承、ES6中的类等相关知识点。

继承

原型属性方式

子构造函数, 可以借用公共构造函数执行, 往子对象中添加父模板中的属性。目的在于把子构造函数都有的属性和方法, 提到公共的构造函数中。举个例子,我们可以把学生类和工人类都有的名字和年龄属性提到人类构造函数中。

// 父类构造函数
      function Person(name, age) {
        this.name = name
        this.age = age
      }
      Person.prototype.say = function () {
        console.log('都会说话')
      }

      function Student(score) {
        this.score = score
      }
      // Person构造函数有自己的原型对象, Student也有自己的原型对象, 将Student的原型对象指向Person的原型对象
      // Student.prototype = Person.prototype
      Student.prototype = new Person('xm', 16)
      var aaa = new Student(100)
      console.log(aaa)
      aaa.say()
      // var xg = new Student(80) 
      // console.log(xg.name);

Student.prototype = Pesron.prototype,将子构造函数的原型对象指向了父构造函数的原型对象, 方法可以调用,属性继承不到。 Student.prototype = new Person('xm', 16) ,将子构造函数的原型对象指向了父构造函数的一个实例, 并且更改的时候传递参数。这种方式叫原型继承,弊端方法可以随便使用,但是属性值继承是一样的,不方便。

借助构造函数继承

借用构造函数继承。在子构造函数内部调用父构造函数,一定更改this指向。创建子构造函数的实例, 将参数传递给子构造函数。借助构造函数继承的弊端在于是属性继承, 方法继承不到。

  // 父类构造函数
      function Person(name, age) {
        // console.log(this) // 此处Person默认当做普通函数调用this指向的是window
        this.name = name
        this.age = age
      }
      Person.prototype.say = function () {
        console.log('都会说话')
      }
      //  子构造函数
      function Student(name, age, score) {
        // Person.apply(this, [name, age]) // 使用apply方法将Person函数调用修改this为Student的实例
        Person.call(this, name, age) // 使用call方法将Person函数调用修改this为Student的实例
        this.score = score
        // console.log(this) // new Student()--> this 就指向了实例
      }
      var zs = new Student('zs', 10, 100)
      var ls = new Student('ls', 38, 89)
      console.log(zs)
      console.log(ls)

寄生组合继承方式

寄生组成继承 = 原型继承 + 构造函数继承,完成了属性的继承和方法的继承。

// 父类构造函数
      function Person(name, age) {
        this.name = name
        this.age = age
      }
      Person.prototype.say = function () {
        console.log('都会说话')
      }
      //  子构造函数
      function Student(name, age, score) {
        Person.call(this, name, age) // 借用构造函数继承
        this.score = score
      }
      Student.prototype.code = function () {
        console.log('写代码要讲码德')
      }
      //   原型继承
      // Student.prototype = Person.prototype
      Student.prototype = Object.create(Person.prototype) // 以Person.prototype创建的一个空对象
      //   打开控制台, 点一下 发现多一层__proto__ 可以解决父构造函数也能调用到子构造函数的原型的方法
      var xm = new Student('xm', 18, 100)
      var xg = new Student('xg', 18, 100)
      console.log(xm)
      console.log(xg)
      xm.say()
      xg.say()
      xm.code()

ES6中的类

类是一组相同特征和行为的事物的抽象 (类似构造函数)。类用来定义一套模板, 批量产生对象 (所以跟构造函数思路一样, 就是写的语法不同)。

创建类

在ES5中, 我们使用的构造函数, 就是在模拟类的思想, 封装一个模板(类), 来批量生成对象。ES6中新增了关键字class, 来定义类, 但是class只是构造函数的语法糖, 底层还是构造函数

// 定义类
class Person {

}

// 实例化对象
var per = new Person();
console.log(per);

constructor

属性定义在constructor函数中。class是类, 并不是对象, 所以每个方法之间, 不要写逗号和分号。class中定义的函数, 为了共享, 会自动挂载到原型属性上

// 定义类
class Person{
    // (可选) 实现构造函数 - 里面定义属性
    constructor (theName, theAge) {
        this.name = theName;
        this.age = theAge;
    }
    // 定义方法
    sayHello () {
        console.log("你好, 我是人类");
    }
}

// 实例化对象
var per = new Person();
console.log(per);

静态属性和静态方法

静态定义使用static修饰。静态调用通过类.xx进行调用,实例调用通过实例对象.xx进行调用。

// 1. 定义属性/方法
class Person{
    static eye = 2; // 静态属性
    constructor (theName, theAge) { // 实例属性
        this.name = theName;
        this.age = theAge;
    }
    static sayHello(){ // 静态方法
        console.log("你好");
    }
    play() { // 实例方法
        console.log("玩电脑");
    }
}

// 2. 调用属性/方法
// 静态调用 - 类调用
console.log(Person.eye);
Person.sayHello();

// 实例调用 - 对象调用
var per = new Person("小黑", 20);
console.log(per.name);
per.play();

extends类继承

子类可以借用父类, 把属性和方法, 添加到子类的对象上, 底层还是组合继承方式

  • 属性继承, 使用call()来蹭父类的属性
  • 方法继承, 使用prototype属性来蹭父类的方法
class Person{
    constructor (theName, theAge) {
        this.name = theName;
        this.age = theAge;
    }
    play(){
        console.log(this.name + "玩游戏");
    }
}
class Boy extends Person{
    constructor (theHeight, tName, tAge) {
        super(tName, tAge); // 必须调用父级(在使用extends时)
        this.height = theHeight;
    }
    run (){
        console.log("男孩会跑");
    }
}
var boy1 = new Boy(185, "小智", 19);
console.log(boy1);

子类super使用

构造函数中使用 super() 会触发父级类的constructor执行。普通函数中使用super.xxx(), 调用父级类里的函数执行

类的本质

class本质还是function。类的所有方法都定义在类的prototype属性上。类创建的实例,里面也有__proto__指向类的prototype原型对象。所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。所以ES6的类其实就是语法糖。