Skip to content

类与面向对象编程

类的基本概念

  • 类(class):定义了一件事物的抽象特点,主要包含了它的属性和方法

  • 对象(Object):类的实例,通过 new 一个类创建

  • 面向对象(OOP)的三大特性:封装、继承、多态

  • 封装(Encasulation):将对数据操作的细节隐藏起来,只暴露对外的接口。外界调用不需要知道细节,也限制更改对象内部数据

  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性

  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应

  • 存取器(getter & setter):用以改变对象属性的读取和赋值行为

  • 修饰符(Modifiers):是一些关键字,用于限定成员的类型和性质。比如public, private, static

  • 抽象类(Abstract Class):提供给其它类继承的基类,不允许被实例化,同时抽象类中的抽象方法必须被子类实现

  • 接口(Interfaces):不同类之间的公共属性和方法,可以抽象成一个接口。接口可以被类实现(Implements)。一个类只能继承自另一个类,但是可以实现多个接口。

ES5 时代设计模式的发展历程(出自红宝书)

工厂模式、构造函数模式

原型继承

js
function Parent() {
  this.isShow = true
  this.info = {
    name: 'yhd',
    age: 18
  }
}

Parent.prototype.getInfo = function () {
  console.log(this.info)
  console.log(this.isShow) // true
}

function Child() {}
// 必须指定原型对象到Parent上,从而让Child可以访问到原型链的方法
Child.prototype = new Parent()

// 创建实例
let Child1 = new Child()
Child1.info.gender = '男'
Child1.getInfo() // {name: "yhd", age: 18, gender: "男"}
function Parent() {
  this.isShow = true
  this.info = {
    name: 'yhd',
    age: 18
  }
}

Parent.prototype.getInfo = function () {
  console.log(this.info)
  console.log(this.isShow) // true
}

function Child() {}
// 必须指定原型对象到Parent上,从而让Child可以访问到原型链的方法
Child.prototype = new Parent()

// 创建实例
let Child1 = new Child()
Child1.info.gender = '男'
Child1.getInfo() // {name: "yhd", age: 18, gender: "男"}

优点

父类方法可以复用,无需重复定义

缺点

  1. 父类的引用属性会被所有子类共享,更改一个子类的引用属性会影响到其它子类

  2. 子类实例不能给父类的构造函数传参

构造函数继承

js
function Parent() {
  this.info = {
    name: 'yhd',
    age: 19
  }
}

function Child() {
  Parent.call(this)
}

let child1 = new Child()
child1.info.gender = '男'
console.log(child1.info) // {name: "yhd", age: 19, gender: "男"};
function Parent() {
  this.info = {
    name: 'yhd',
    age: 19
  }
}

function Child() {
  Parent.call(this)
}

let child1 = new Child()
child1.info.gender = '男'
console.log(child1.info) // {name: "yhd", age: 19, gender: "男"};

优点

  1. 可以通过子类的构造函数,向父类构造函数传参

  2. 父类的引用属性不会被共享

缺点

子类不能访问父类原型上定义的方法,从而导致每次创建实例的时候都会重新创建方法

组合继承

这种方式其实就是将原型继承和***构造函数继承**的优点结合起来

js
function Parent() {
  this.name = name
  this.colors = ['red', 'blue', 'yellow']
}
Parent.prototype.sayName = function () {
  console.log(this.name)
}

function Child() {
  Parent.call(this, name)
  this.age = age
}
Child.prototype = new Parent()
Child.prototype.sayAge = function () {
  console.log(this.age)
}

// 实例化
let child1 = new Child('xiao ming', 19)
child1.sayAge() // 19
function Parent() {
  this.name = name
  this.colors = ['red', 'blue', 'yellow']
}
Parent.prototype.sayName = function () {
  console.log(this.name)
}

function Child() {
  Parent.call(this, name)
  this.age = age
}
Child.prototype = new Parent()
Child.prototype.sayAge = function () {
  console.log(this.age)
}

// 实例化
let child1 = new Child('xiao ming', 19)
child1.sayAge() // 19

优点

  1. 父类方法可以复用

  2. 可以在子类构造函数中向父类构造函数传参

  3. 父类构造函数中的引用属性不会被共享

寄生式继承

使用原型式继承对一个目标对象进行浅复制,同时增强浅复制的能力

js
function objectCopy(obj) {
  function Fun() {}
  Fun.prototype = obj
  return new Fun()
}

function createAnother(original) {
  let clone = objectCopy(original)
  clone.getName = function () {
    console.log(this.name)
  }
  return clone
}
let person = {
  name: 'jack',
  friends: ['rose', 'tom', 'jack']
}

let person1 = createAnother(person)
person1.friends.push('lily')
console.log(person1.friends)
person1.getName() // jack

let person2 = createAnother(person)
console.log(person2.friends) // ["rose", "tom", "jack", "lily"]
function objectCopy(obj) {
  function Fun() {}
  Fun.prototype = obj
  return new Fun()
}

function createAnother(original) {
  let clone = objectCopy(original)
  clone.getName = function () {
    console.log(this.name)
  }
  return clone
}
let person = {
  name: 'jack',
  friends: ['rose', 'tom', 'jack']
}

let person1 = createAnother(person)
person1.friends.push('lily')
console.log(person1.friends)
person1.getName() // jack

let person2 = createAnother(person)
console.log(person2.friends) // ["rose", "tom", "jack", "lily"]

寄生组合式继承

js
function objectCopy(obj) {
  function Fun() {}
  Fun.prototype = obj
  return new Fun()
}

function inheritPrototype(child, parent) {
  let prototype = objectCopy(parent.prototype) // 创建对象
  prototype.constructor = child // 增强对象
  Child.prototype = prototype // 赋值对象
}

function Parent(name) {
  this.name = name
  this.friends = ['rose', 'lily', 'tom']
}

Parent.prototype.sayName = function () {
  console.log(this.name)
}

function Child(name, age) {
  Parent.call(this, name)
  this.age = age
}

inheritPrototype(Child, Parent)
Child.prototype.sayAge = function () {
  console.log(this.age)
}

let child1 = new Child('yhd', 23)
child1.sayAge() // 23
child1.sayName() // yhd
child1.friends.push('jack')
console.log(child1.friends) // ["rose", "lily", "tom", "jack"]

let child2 = new Child('yl', 22)
child2.sayAge() // 22
child2.sayName() // yl
console.log(child2.friends) // ["rose", "lily", "tom"]
function objectCopy(obj) {
  function Fun() {}
  Fun.prototype = obj
  return new Fun()
}

function inheritPrototype(child, parent) {
  let prototype = objectCopy(parent.prototype) // 创建对象
  prototype.constructor = child // 增强对象
  Child.prototype = prototype // 赋值对象
}

function Parent(name) {
  this.name = name
  this.friends = ['rose', 'lily', 'tom']
}

Parent.prototype.sayName = function () {
  console.log(this.name)
}

function Child(name, age) {
  Parent.call(this, name)
  this.age = age
}

inheritPrototype(Child, Parent)
Child.prototype.sayAge = function () {
  console.log(this.age)
}

let child1 = new Child('yhd', 23)
child1.sayAge() // 23
child1.sayName() // yhd
child1.friends.push('jack')
console.log(child1.friends) // ["rose", "lily", "tom", "jack"]

let child2 = new Child('yl', 22)
child2.sayAge() // 22
child2.sayName() // yl
console.log(child2.friends) // ["rose", "lily", "tom"]