重学JavaScript高级(七):ES6中实现继承的方法以及JS中的多态

本文详细介绍了ES6中通过class实现继承的方法,包括构造函数、静态方法、访问器方法的使用,以及类的继承、静态方法重写和多态概念。同时提到了如何通过mixin解决多继承问题,以及Babel转换ES6代码的实际应用。
摘要由CSDN通过智能技术生成

ES6实现继承

在前一篇的文章,我们学习了ES5通过原型 原型链实现继承的方式,那么ES6中是如何实现继承,其背后的原理又是怎样的?

建议在看这篇文章之前,要先阅读我的前一篇文章,会让你有醍醐灌顶的想法
重学JavaScript高级(六):以面向对象原型继承(ES5)搞懂原型原型链

认识class定义类

  • ES5之前定义类的方式,使用Function进行定义,这种方法和 普通函数过于相似
  • 所以在ES6中将class提升到了关键字
  • new Person后,其内部的操作,与ES5的操作一致(具体操作可以看先前的文章
  • 以下是定义方法
//拥有高内聚,低耦合的特点
class Person{
    constructor(name,age){
        //用于接收参数,默认调用
        this.name= name
        this.age = age
    }
    //这是实例方法,实际上是将方法添加到了Person.prototype中,是一种语法糖
    running(){
        console.log(this.name+"running")
    }
}
let p1 = new Person("zhangcheng",18)
console.log(p1.__proto__ === Person.prototype)

与function的区别

  • 通过以下代码,可以看出class定义的类,就是function定义类的语法糖,几乎一模一样
  • 但是class定义的类,不能当作单独的函数调用,只能和new一起使用
//拥有高内聚,低耦合的特点
class Person1{
    constructor(name,age){
        //用于接收参数,默认调用
        this.name= name
        this.age = age
    }
    //这是实例方法,实际上是将方法添加到了Person.prototype中,是一种语法糖
    running(){
        console.log(this.name+"running")
    }
}

//构造函数Person
function Person2(name) {
  this.name = name;
}
//只能通过p1调用
Person.prototype.study = function () {
  console.log("123");
};

let p1 = new Person1("zhangcheng",18)
let p2 = new Person2("lisi")

console.log(Person1.prototype === p1.__proto__)//true
console.log(Person2.prototype === p2.__proto__)//true
console.log(Person1.prototype.constructot) //Person1
console.log(Person2.prototype.constructot) //Person2

类的访问器方法(access)

监听对某个属性的读取以及写入

  • 先来回顾对象中访问器方法的编写
//通过存储属性描述符进行编写
let obj = {
  name: "zhangcheng",
};

let _name = "";
Object.defineProperty(obj, "name", {
  set: function (value) {
    _name = value;
    console.log("set方法被调用了");
  },
  get: function () {
    console.log("get方法被调用了");
    return _name;
  },
});

obj.name = "lisi";
console.log(obj.name);

  • 以下就是类的访问器编写方法
    • 实际开发中用的比较少
class Person1 {
  constructor(name, age) {
    //用于接收参数,默认调用
    this.newName = name;
    this.age = age;
  }
  //这是实例方法,实际上是将方法添加到了Person.prototype中,是一种语法糖
  running() {
    console.log(this.name + "running");
  }
//在定义set的时候,一定要传入参数,否则会报错
  set newName(value) {
    this.name = value;
  }
  get newName() {
    return this.name;
  }
}

let p1 = new Person1("zhangcheng", 123);
console.log(p1.newName);
  • 具体应用场景
    • 类的访问器主要用在数据处理上面
    • 比如我们现在写一个矩形的类,需要传入它的位置信息和大小信息
class Shape {
  constructor(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
  }

  // position属性返回x值和y值
  get position() {
    return { x: this.x, y: this.y };
  }

  //size属性返回widht值和height值
  get size() {
    return { width: this.width, height: this.height };
  }
}

let s1 = new Shape(10, 20, 100, 200);
console.log(s1.position); //{ x: 10, y: 20 }
console.log(s1.size);//{ width: 100, height: 200 }

类的静态方法(ES5的类方法)

通过类直接调用Person().running

  • 在类里面写方法的时候,前面加上static即可
class Person {
  constructor(name) {
    this.name = name;
  }

  static personRunning() {
    //这里的this指向的就是Person类本身
    console.log("personRunning");
  }
}
//类直接进行调用
Person.personRunning();

通过extends实现继承

  • 首先 定义一个父类
  • 子类在后面通过 extends进行继承父类
  • 在子类的构造方法中(constructor)使用super关键字,调用父类的 属性
class Person {
  constructor(pName, age) {
    this.pName = pName;
    this.age = age;
  }

  running() {
    console.log("running");
  }
}

class Teacher extends Person {
  constructor(pName, age, title) {
    //super一定要用在子类this前面
    super(pName, age);
    this.title = title;
  }

  teach() {
    console.log("teach");
  }
}

class Student extends Person {
  constructor(pName, age, sno) {
    //super一定要用在子类this前面
    super(pName, age);
    this.sno = sno;
  }
  study() {
    console.log("study");
  }
}

let tea1 = new Teacher("wanglaoshi", 58, "教授");
let stu1 = new Student("zhangcheng", 18, 200);
console.log(tea1);
console.log(stu1);
tea1.running();
stu1.running();
  • super不仅可以在子类的构造方法中使用,也可以在实例方法和静态方法中使用
    • 需要注意:子类的实例方法只能用super调用父类的实例方法,子类的静态方法亦是如此
class Person {
  constructor(pName, age) {
    this.pName = pName;
    this.age = age;
  }

  running() {
    console.log("running");
  }

  static walk() {
    console.log("walk");
  }
}

class Student extends Person {
  constructor(pName, age, sno) {
    //super一定要用在子类this前面
    super(pName, age);
    this.sno = sno;
  }
  //实例方法
  study() {
    console.log("study");
    //只能用super调用父类的实例方法
    super.running();
  }

  static play() {
    console.log("play");
    //只能用super调用父类的静态方法
    super.walk();
  }
}

let stu1 = new Student("zhangcheng", 18, 200);
stu1.study();
Student.play();
  • 重写的理解

    当我们对父类的方法,不满意的时候,在子类中可以重新编写一下这个方法,就叫重写

class Person {
  constructor(pName, age) {
    this.pName = pName;
    this.age = age;
  }

  running() {
    console.log("running");
  }

  static walk() {
    console.log("walk");
  }
}

class Student extends Person {
  constructor(pName, age, sno) {
    //super一定要用在子类this前面
    super(pName, age);
    this.sno = sno;
  }
  //实例方法重写
  running() {
    console.log("Student running");
  }

  //静态方法重写
  static walk() {
    console.log("Student walk");
  }
}

let stu1 = new Student("zhangcheng", 18, 200);
stu1.running();
Student.walk();

继承内置类

比如继承js提供的内置类,对一些方法进行扩展

class ZcArray extends Array {
    //打印数组的最后一位
  lastItem() {
     //这里的this指向的就是调用方法的实例,隐式绑定
      //不理解的可以去看我的第一篇文章,this指向问题
    console.log(this[this.length - 1]);
  }
}

let arr = new ZcArray(10, 20, 30);
arr.lastItem();

类的混入mixin(用的不多)

JS的继承只能单继承,意思就是一个子类只能有一个父类

注意:主要是掌握这种思想,在后期React中,高阶组件会有类似的写法

  • 现在有三个类Bird、Flyer、Animals,需要实现Bird要继承Flyer、Animals
class Animals {
  running() {
    console.log("running");
  }
}

class Flyer {
  fly() {
    console.log("flying");
  }
}

class Bird {
  getName() {
    console.log("my name is bird");
  }
}
  • 其中一个方法就是,借助 mixin的思想
    • 实际上是通过创建多个类,去一层一层的继承
function mixinAnimals(BaseClass) {
  return class extends BaseClass {
    running() {
      console.log("running");
    }
  };
}

function mixinFlyer(BaseClass) {
  return class extends BaseClass {
    fly() {
      console.log("flying");
    }
  };
}

class Bird {
  getName() {
    console.log("my name is bird");
  }
}

class NewBird extends mixinAnimals(mixinFlyer(Bird)) {}

let bird = new NewBird();
bird.running();
bird.fly();
bird.getName();

image.png

babel可以将ES6转成ES5代码

  • 大家可以访问https://babeljs.io/,将ES6的class类代码,转成ES5代码

JavaScript中的多态

面向对象的多态

继承是多态的前提

  • 维基百科中的定义
    • 多态指的是为不同数据类型的实体,提供统一的接口,或者使用一个单一的符号,来表示多个不同的类型
  • 个人的理解
    • 不同的数据类型,进行同一个操作(访问同一个接口)表现出不同的行为(得出不同的结果),就是多态的表现
  • 在下面的代码中,我们就实现了将不同的数据类型,传入同一个函数,得出了不同的结果
/*
1.定义一个父类Shape
2.定义两个子类均继承父类
3.写一个函数,里面均让传入的参数调用getArea方法
4.实例出两个子类的对象,并传入函数中
5.这种表现形式可以称为多态
*/
class Shape {
  getArea() {}
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }
  getArea() {
    return this.width * this.height;
  }
}

class Circle extends Shape {
  constructor(r) {
    super();
    this.r = r;
  }
  getArea() {
    return this.r * this.r * 3.14;
  }
}

/**
 严格意义的面向对象中
  1.必须有继承
  2.必须有父类引用指向子类对象
 */

function getShapeArea(shape) {
  //在严格的面向对象编程语言中,形参是有类型控制的,类似于shape:Shape,传入的参数必须是Shape类型的,在此就是父类引用
  console.log(shape.getArea());
}

let rect1 = new Rectangle(10, 10);
let circle = new Circle(10);
getShapeArea(rect1);
getShapeArea(circle);

JS面向对象多态的表现

从维基百科的定义出发,那么JS中到处都是多态

  • JS中针对不同数据类型,提供统一的接口,表现不同的行为
function sum(a,b){
    return a+b
}
sum(200,300)
sum("hello",123)
  • 使用一个单一的符号,来表示多个不同的类型
//同一个标识符foo
let foo = 123
foo = "zhangcheng"
foo = {
    a:100
}
  • 对于多态的理解不同人有不同的看法,有的人认为这种不算严格意义上的多态,因此以上只是我个人的理解
  • 26
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值