你真的会用extends吗?

为什么我突然关注起了extends呢?
最近,在支援flutter项目的时候,由于我们是多人开发,有些模块的逻辑又是可以共用的,于是,T7大佬说,你用我这个extends一下再override一下就好了,你不用写这块逻辑了

在这里插入图片描述

extends ? Dart中的继承和Vue有啥区别、我Vue很少用extends啊,我一般用mixin,于是便有了这篇文章!

设计模式:继承&组合

我们先来搞清楚两个概念,继承和组合,在面向对象编程中,继承和组合是两种常用的代码复用方式,但随Vue、React等框架的发展,作为前端工程师的我,对类的概念逐渐模糊,而转化成了业务场景中的:组件封装、逻辑复用。

继承

子类继承父类的特征和行为,使得子类具有和父类一样的属性和方法

  • 优点
    代码复用。
    多态性:继承父类的方法,子类可以根据自己的需求重写父类方法
    is-a关系,可以用来约束父类和子类的关系。
  • 缺点
    类间的层次变深会影响代码的可读性和可维护性。
    子类会继承了父类的所有属性和方法,即使没有使用也会继承,容易造成自身属性的膨胀

组合

组合是对现有对象进行拼装组合实现更复杂的功能,

  • 优点
    has-a的关系,表明自身这个类包含其他类的关系。
    继承的特性可以通过组合、接口、委托实现。解决层次过深、过复杂的继承关系影响代码可维护性的问题。
    组合的层级关系更少,类间的耦合性更低,便于代码维护和阅读。
  • 缺点
    将继承改为组合,意味着需要进行更细粒度的拆分,势必会产生更多的类和接口,类的数量增加会增加代码维护成本。

在《设计模式之美》一书中,认为“多用组合,少用继承”

接下来我们对比Dart和Vue

Dart

Dart作为面向对象语言,和Java类似

extends

  • 单继承、多态性
  • 子类重写超类的方法,用@override
  • 子类调用超类的方法,用super

当你使用 extends 继承一个抽象类时没有必要覆写抽象类中所有的方法, 但是抽象类中的 getter 和 setter 是必须覆写的

import 'Person.dart';
class Student extends Perosn{
 
  // 覆写父类的计算属性
  bool get adult => this.age > 15;
 
  void study(){
    print("Student studying...");
  }
 
  
  void run() {
    // 调用父类的方法
    super.run();
    print("student running...");
  }
}
 
void main(){
  Student student = new Student();
  student.age = 16;
  student.run();    //  Person running...       student running...
  print(student.adult); //  true
 
}

implements

dart中没有专门的interface去创建接口,一般是用抽象类创建接口
注意:使用接口,必须覆写父类中每一个方法

abstract class InterfaceOne{
  void one();
}
abstract class InterfaceTwo{
  void two();
}
class Example implements InterfaceOne,InterfaceTwo{
  
  void one() {
  }
  
  void two() {
  }
}

mixin

混入,多个类可以混入到一个类中,这样就可以实现类似多继承的效果。
mixin存在冲突的部分,后面会覆盖前面的,没有冲突的则会保留,所以可以存在后面的mixin修改了前面的mixin的一部分逻辑的情况,不需要直接继承即可实现覆盖,避免了更复杂的继承关系

// 一、直接继承Object
abstract class A {
  void initInstances() {
    print("A——initInstances");
  }
}

mixin B on A{

void initInstances() {
  print("B——initInstances");
  super.initInstances();
}
}
// 二、间接继承extends关键字后面的类
class C extends B with A,D{
    //
}

Vue

extends

全局API,使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象。
只能单次扩展一个组件

在官方给的例子中

<div id="mount-point"></div>

<script>
// 创建构造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')
</script>

什么时候使用?

常用于独立组件开发场景,比如elementUI中

  • Vue.extend + vm.$mount 组合
// 假设我们已经实现了loading组件
import Vue from 'vue'
import Loading from './loading'

// 通过Vue的extend方法继承这个引入的 loading 组件,继承后会返回一个vue子类,需要使用实例化即可
const LoadingConstructor = Vue.extend(Loading)

// 创建实例并且挂载到 div上
const loading = new LoadingConstructor().$mount(document.createElement('div'))

// 显示loading效果
function showLoad (options) {
  // 初始化调用传递过来的参数赋值更改组件内内部值
  for (const key in options) {
    loading[key] = options[key]
  }
  // 让其显示
  loading.showLoading = true
  // 并将 Vue.extend 创建的 dom 元素插入body中
  document.getElementById('app').appendChild(loading.$el)
}

// 关闭loading效果
function hideLoad () {
  loading.showLoading = false
}

// 将控制 loading 的方法挂载到 Vue 原型
Vue.prototype.$showLoad = showLoad
Vue.prototype.$hideLoad = hideLoad

  • Vue.extend + vm.$on 组合
// 假设我们已经实现了toast组件
import Vue from 'vue'
import Toast from './toast.vue'

// 创建Toast构造器
const ToastConstructor = Vue.extend(Toast)
let instance

function toast (options = {}) {
  // 设置默认参数为对象,如果参数为字符串,参数中message属性等于该参数
  if (typeof options === 'string') {
    options = {
      message: options,
    }
  }
  // 创建实例
  instance = new ToastConstructor({
    data: options,
  })
  
  // 注册组件的监听事件
  instance.$on('close-event', () => {
    console.log('success')
  })
  
  // 将实例挂载到body下
  document.body.appendChild(instance.$mount().$el)
}

// 将Toast组件挂载到vue原型上
Vue.prototype.$toast = toast

mixin

  • 多个组件共享数据和方法,可以局部混入和全局混入
  • 一个组件中改动了mixin的数据,另一个组件不会受影响
  • 命名冲突、不好追溯源,排查复杂
使用(以局部混入为例)

1、定一个mixin

export const mixins = {
  data() {
    return {};
  },
  computed: {},
  created() {},
  mounted() {},
  methods: {},
};

2、在文件中混入

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <button @click="clickMe">点击我</button>
  </div>
</template>

<script>
import { mixins } from "./mixin/index";
export default {
  name: "App",
  mixins: [mixins],
  components: {},
  created(){
    console.log("组件调用minxi数据",this.msg);
  },
  mounted(){
    console.log("我是组件的mounted生命周期函数")
  }
};
</script>


选项合并

1、data数据冲突:组件中的data数据会覆盖mixin中的数据,无冲突的部分自然合并
2、methods、components冲突:组件覆盖mixin,无冲突自然合并
不过 我们可以自定义合并规则,这里不延伸
3、生命周期钩子:两者都会执行,并且mixin中先执行

最后,通过对比Vue和Dart,我对extends的使用方法了解的更加深刻了!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值