什么是JavaScript的面向对象?

183 阅读3分钟

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

面向对象编程,Object-Oriented-Program(简称 OOP)是一种目前主流的编程思想。已有几十年的历史,1990 年代开始,和 Java 一起开始发展壮大。

编程本来是抽象的,像做数学题一样。 一开始的汇编语言,直接操作寄存器、内存,写底层计算。后来的 C 语言,各种函数和指针。

而 OOP 引入了"对象"概念,对象即对应生活中的实物,这样就把编程具象化了。具象化之后学习成本就低了,也就随着计算机革命普及开来。

比如要实现一个停车场的功能,如果没有"对象"的概念,那我们编程就直接操作内存、命令、指针、寄存器等。有了对象之后,就可以把停车场里面的车位、楼层、出入口显示屏等实物全部对象化,直接操作对象即可,这样我们就能更好的学习编程,更高效的开发。还有现在那么多的应用,比如打车,外卖等业务,其实都是看成一个对象进行编程的。

设计模式就是基于 OOP 编程思想的,不适用于其他编程思想(如函数式编程)。

类和对象

类:即模板

class People {
    name: string
    age: number

    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }

    // 如果函数不写返回值类型,则默认为 void
    eat() {
        alert(`${this.name} eat something`)
    }

    speak() {
        alert(`My name is ${this.name}, age ${this.age}`)
    }
}

对象:即实例

一个类可以 new 出很多个对象。

// 创建实例
let zhang = new People('zhang', 20)
zhang.eat()
zhang.speak()

// 创建实例
let wang = new People('wang', 21)
wang.eat()
wang.speak()

面向对象三要素

继承

抽离公共代码,实现代码复用。

class Person {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  eat() {
    alert(`${this.name} eat something`)
  }
  speak() {
    alert(`My name is ${this.name}, age ${this.age}`)
  }
}
// 继承
class Student extends Person {
  school: string
  constructor(name: string, age: number, school: string) {
    super(name, age)
    this.school = school
  }
  study() {
    alert(`${this.name} study`)
  }
}
// 继承
class Teacher extends Person {
  major: string
  constructor(name: string, age: number, major: string) {
    super(name, age)
    this.major = major
  }
  teach() {
    alert(`${this.name} teach ${this.major}`)
  }
}

继承的好处就是代码的复用,同时,当代码有修改的时候只要改动一处就可以了。

封装

封装的意思就是可以把某些信息隐藏起来,这样为了实现高内聚,低耦合

可以把一个系统想象成一个船,每个船舱就好比一个模块,船舱和船舱之间是相互隔离的(低耦合),如果一个船舱进水了,那么不影响其他船舱(低耦合),只需要把进水的船舱修好就可以了,它坏只会坏在它自己内部(高内聚)。当你写代码的时候,尽量让内聚多一些,让耦合少一些。

那如何实现高内聚低耦合呢?就通过可见性修饰符

  • public: 外部可访问,默认
  • protected: 内部或子类可访问
  • private: 只有内部可访问
class Person {
  name: string
  age: number
  // 保护 即 封装
  protected weight: number = 100
  private girlfriend: string = 'xiaofang'
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  eat() {
    alert(`${this.name} eat something`)
  }
  speak() {
    alert(`My name is ${this.name}, age ${this.age}`)
  }
}

在person这个对象里面,我想对weightgirlfriend进行保护(封装),那么就可以使用可见性修饰符,不让外界访问。

多态

多态:保证扩展性。一个功能做完之后,后面可能需要修改和扩展,因此,需要保证扩展性。

一般表现在子类对父类的修改,有两种方式来实现:

  • 重写:重复父类的eat方法
class Student extends Person {
  school: string
  constructor(name: string, age: number, school: string) {
    super(name, age)
    this.school = school
  }
  study() {
    alert(`${this.name} study`)
  }
  // 重写
  eat() {
    alert(`${this.name} eat apple`)
  }
}
  • 重载
interface IStyleInfo {
  [key: string]: string
}

class Jquery {
  css(key: string, value: string): void
  css(styleInfo: IStyleInfo): void
  css(keyOrInfo: string | IStyleInfo, value?: string) {
      if (typeof keyOrInfo === 'string') {
          // key value
      } else {
          // object
      }
  }
}

const jq = new Jquery()
jq.css({ 'font-weight': 'bold', color: 'red' })

const jq = new Jquery()
jq.css({ 'font-weight': 'bold', color: 'red' })
jq.css('color', 'red')

css方法传入的参数:

  • 以 key / value 的形式传入
  • 以对象 IStyleInfo 的形式传入

写重载的时候,最后面写的一定要兼容前面几个参数,比如css(keyOrInfo: string | IStyleInfo, value?: string)就兼容了前面两个参数形式,否则会报错。

细节

  • 继承: super
  • 封装: 可见性修饰符 protected privated
  • 多态: 重写或者重载(一个函数多参数类型)

Vue和React组件也是对象

组件其实就是一个类,当你使用组件的时候就是在实例化这个类。

react

class MyComponent extends React.Component {
  constructor() {
    // state
  }
  componentDidMounted() {}
  render() {
    // jsx
  }
}
// JSX 使用
<MyComponent></MyComponent>
// 使用组件其实就是实例化了一个组件对象
const c = new MyComponent

Vue

<!-- 定义一个 SomeComponent.vue 组件 -->

<!-- page1 -->
<template>
    // 使用的时候也是实例化了一个SomeComponent对象
    <some-component :name="a"></some-component>
</template>

<!-- page2 -->
<template>
    <some-component :name="b"></some-component>
</template>