面向对象
概念
类
,具有相同或相似属性或方法的对象的抽象就是类。例如:人都有姓名、年龄,都会吃饭、说话,其中姓名、年龄就是人这个类的属性,吃饭、说话就是人这个类的方法。对象
,类的具体实现就是对象。例如,小明这个人就可以认为是人这个类的具体实现。
// 类
class Person {
constructor(name, age) {
this.name = name // 属性 姓名
this.age = age // 属性 年龄
}
// 方法 吃饭
eat() {
alert(`${this.name} eat something.`)
}
// 方法 说话
speak() {
alert(`My name is ${this.name}, my age is ${this.age}.`)
}
}
// 对象 Person的具体实现
const xiaoming = new Person('xiaoming', 10)
xiaoming.eat()
xiaoming.speak()
三要素
继承
,子类可以继承父类的属性和方法。例如,学生是人类的一种,具有人类的所有特性,也可以有自己的特性,可以认为学生继承了人这个父类。
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
eat() {
alert(`${this.name} eat something.`)
}
speak() {
alert(`My name is ${this.name}, my age is ${this.age}.`)
}
}
// Student 继承了 Person,具有 Person 的所有属性,并且有自己的特有属性
class Student extends Person {
constructor(name, age, no) {
super(name, age)
this.no = no // 学生可以有学号属性
}
// 学生可以学习
study() {
alert(`${this.name} study something.`)
}
}
const xiaoming = new Student('xiaoming', 10, '10010')
xiaoming.study()
封装
,类的属性具有访问权限。说到封装不得不说public、protected、private
三个关键字,其中public
关键字修饰的属性表示公有的属性,可以随便访问,protected
关键字修饰的属性表示子类内部可以访问,private
关键字修饰的属性只能在类内部访问,外部无法访问(图中+
表示共有属性、#
表示子类可以访问、-
表示私有属性,由于 javascript 不支持这三种修饰符,所以此处用 typescript 演示,了解即可)。
class Person {
public name // 公有属性,可以随便访问
public age
protected weight // 子类内部可以访问,外部无法访问
constructor(name, age) {
this.name = name
this.age = age
this.weight = 120
}
eat() {
console.log(`${this.name} eat something.`)
}
speak() {
console.log(`My name is ${this.name}, age is ${this.age}.`)
}
}
class Student extends Person {
private grilfriend // 私有属性,只有在 Student 类内部才可以访问
constructor(name, age) {
super(name, age)
this.grilfriend = 'xiaoli'
}
study() {
alert(`${this.name} study.`)
}
getWeight() {
alert(`weight ${this.weight}.`) // protected 修饰的属性子类内部可以访问
}
}
let xiaoming = new Student('xiaoming', 10)
xiaoming.getWeight()
alert(xiaoming.name)
// alert(xiaoming.weight) // 报错
// alert(xiaoming.grilfriend) // 报错
多态
,子类既可以继承父类的属性和方法,也可以有自己的特性。学生是人类的一种,除了具有人类的所有特性之外,还可以有自己独特的特性,例如学生可以进行学习这个动作。
class Person {
constructor(name) {
this.name = name
}
saySomething() {}
}
class A extends Person {
constructor(name) {
super(name)
}
saySomething() {
alert('This is A.')
}
}
class B extends Person {
constructor(name) {
super(name)
}
saySomething() {
alert('This is B.')
}
}
五大设计原则
单一职责原则
,应该有且只有一个引起变更的原因,一个程序只做一件事情,如果功能过于复杂,就要拆开,每部分保持独立。开放封闭原则
,一个软件的实体,比如模块、类、对象应该对扩展开放,对修改封闭,增加新需求时要增加代码而不是修改代码。里氏置换原则
现的地方,子类就可以出现,且替换成子类也不会产生任何错误或者异常。接口独立原则
接口,不要建立臃肿庞大的接口。即接口尽量细化,同时接口中的方法尽量少。依赖倒置原则
,面向接口编程,模块间的依赖关系通过接口和抽象类产生,实体类之间不直接发生依赖关系。最少知识原则
统提供一个简单的入口,访问者或者客户不用了解系统内部的实现细节,只要懂得如何调用就好。
工厂模式
定义一个创建对象的工厂,将创建者和构造函数分离,创建者不用关心具体构造函数的实现,符合开放封闭原则和最少知识原则。
class Product {
constructor(name) {
this.name = name
}
init() {
console.log('init.')
}
fn() {
console.log('fn.')
}
}
class Creator {
create(name) {
return new Product(name)
}
}
const creator = new Creator()
const obj = creator.create('obj')
obj.init()
obj.fn()
单例模式
系统中被唯一使用,一个类只有一个示例,例如:
- 登录框
- 购物车
class SingleObject {
constructor() {}
init() {
console.log('init...')
}
}
SingleObject.getInstance = (function() {
let instance = null
return function() {
if (!instance) {
instance = new SingleObject()
}
return instance
}
})()
const obj1 = SingleObject.getInstance()
obj1.init()
const obj2 = SingleObject.getInstance()
obj2.init()
console.log(obj1 === obj2)
适配器模式
当一个新的方法需要兼容旧的调用方式时,中间需要加一个适配器转换接口。例如,我们电脑的正常电压是24V,而家用正常电压是220V,这时候我们需要一个电源适配器来链接我们的电脑和家用插座,来达到正常工作的目的。
class Adaptee {
specifiRequest() {
return '标准220V电源'
}
}
class Target {
constructor() {
this.adaptee = new Adaptee()
}
request() {
return `${this.adaptee.specifiRequest()} >> 电脑24V标准电源`
}
}
const target = new Target()
console.log(target.request())
装饰器模式
在不改变现有对象的前提下,给现有对象添加新的功能,符合开放封闭原则。
class Circle {
draw() {
console.log('画一个圆形')
}
}
class Decorator {
constructor(circle) {
this.circle = circle
}
setRedBorder() {
console.log('设置红色边框')
}
draw() {
this.circle.draw()
this.setRedBorder()
}
}
let circle = new Circle()
let decorator = new Decorator(circle)
decorator.draw()
ES7
装饰器,首先需要安装babel-plugin-transform-decorators-legacy
,执行npm install babel-plugin-transform-decorators-legacy
,配置.babelrc
,添加 "plugins": ["transform-decorators-legacy"]
。
// 装饰类 mixins
function mixins(...args) {
return function(target) {
Object.assign(target.prototype, ...args)
}
}
const obj = {
foo() {
console.log('foo')
}
}
@mixins(obj)
class MyClass {}
let myClass = new MyClass()
myClass.foo()
// 装饰方法,例1 readonly
function readonly(target, name, descriptor) {
descriptor.writable = false
}
class Person {
constructor() {
this.first = 'A'
this.last = 'B'
}
@readonly
name() {
return `${this.first} ${this.last}`
}
}
const person = new Person()
console.log(person.name())
// person.name = function() {} // 报错
// 装饰方法,例2 log
function log(target, name, descriptor) {
const oldValue = descriptor.value
descriptor.value = function() {
console.log(`Calling ${name} with`, ...arguments)
return oldValue.apply(this, arguments)
}
}
class Math {
constructor() {
this.c = 3
}
@log
add(a, b) {
return a + b + this.c
}
}
const m = new Math()
console.log(m.add(1, 2))
安装第三方库core-descorators
,执行npm install descorators
// 第三方库 core-dascorators
import { readonly, deprecate } from 'core-decorators'
class Person {
@readonly
@deprecate('name 方法即将废弃', { url: 'http://www.baidu.com' })
name() {
return 'zhangsan'
}
}
let p = new Person()
console.log(p.name())
// p.name = function() {}
代理模式
使用者无法或者不方便直接访问目标对象,此时,中间适合加一个代理对象,通过代理对目标对象进行访问,例如:
- 国内网络环境无法直接访问
google
,这时需要通过vpn进行访问 - 想要邀请某个明星进行演出时,无法直接联系明星本人,必须通过其经纪人进行联系
class ReadImg {
constructor(fileName) {
this.fileName = fileName
this.loadFromDisk()
}
loadFromDisk() {
console.log('loading...' + this.fileName)
}
display() {
console.log('display...' + this.fileName)
}
}
class ProxyImg {
constructor(fileName) {
this.readImg = new ReadImg(fileName)
}
display() {
this.readImg.display()
}
}
let proxyImg = new ProxyImg('1.png')
proxyImg.display()
ES6
代理,拿明星和经纪人的示例进行演示。
// 明星
const star = {
name: '张xxx',
age: 26,
phone: '13100000000'
}
// 经纪人
const agent = new Proxy(star, {
get(target, key) {
if (key === 'phone') {
// 返回经纪人自己的手机号
return '13888888888'
}
if (key === 'price') {
// 明星不报价,经纪人报价
return 120000
}
return target[key]
},
set(target, key, value) {
if (key === 'customPrice') {
if (value < 100000) {
throw new Error('价格太低!')
} else {
target[key] = value
return true
}
}
}
})
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)
agent.customPrice = 90000
console.log(agent.customPrice)
代理模式、适配器模式、装饰器模式比较
代理模式 vs 适配器模式,代理模式提供一模一样的接口(使用者无权使用原来的接口),适配器模式提供不同的接口(原来的接口无法使用)
代理模式 vs 装饰器模式,代理模式显示原有功能,但是经过限制和阉割之后,装饰器模式原有功能不变且可直接使用,扩展功能
外观模式
它为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。引入外观角色之后,使用者只需要直接与外观角色交互,使用者与子系统之间的复杂关系由外观角色来实现,从而降低了系统的耦合度。简化复杂接口,解耦和,屏蔽使用者对子系统的直接访问。例如:
- 在家要看电影,需要打开音响,再打开投影仪,再打开播放器等等,引入外观角色之后,只需要调用“打开电影设备”方法就可以。外观角色封装了打开投影仪等操作,给使用者提供更容易使用的方法。
function a(x) {
// doSomething
}
function b(y) {
// doSomething
}
function ab(x, y) {
a(x)
b(y)
}
观察者模式
观察者模式又叫发布—订阅模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,所有依赖于它的对象都将得到通知。例如:
- 小明最近看上了一套房子,但是到了售楼处才被告知,该楼盘的房子早已售罄,好在售楼 MM 告诉小明,不久后还有一些尾盘推出,于是小明离开之前,把电话号码留在了售楼处。售楼 MM 答应他,新楼盘一推出就马上发信息通知小明。小红、小强和小龙也是一样,他们的电话号码都被记在售楼处的花名册上,新楼盘推出的时候,售楼 MM 会翻开花名册, 遍历上面的电话号码,依次发送一条短信来通知他们。
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.nodifyAllServers()
}
attach(observer) {
this.observers.push(observer)
}
nodifyAllServers() {
this.observers.forEach(observer => observer.update())
}
}
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(`${this.name} update, state is ${this.subject.getState()}`)
}
}
const s = new Subject()
const o1 = new Observer('o1', s)
const o2 = new Observer('o2', s)
const o3 = new Observer('o3', s)
s.setState(1)
s.setState(2)
s.setState(3)
迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。
class Iterator {
constructor(container) {
this.list = container.list
this.index = 0
}
done() {
return this.index >= this.list.length
}
next() {
if (!this.done()) {
return this.list[this.index++]
}
return null
}
}
class Containter {
constructor(list) {
this.list = list
}
getGenerator() {
return new Iterator(this)
}
}
const container = new Containter(['xxx', 'yyy', 'zzz'])
const generator = container.getGenerator()
let ret
while ((ret = generator.next())) {
console.log(ret)
}
状态模式
将状态封装成独立的类,并将请求委托给当前的状态对象,当对象的内部状态改变时,会带来不同的行为变化。
class State {
constructor(color) {
this.color = color
}
handle(context) {
console.log(`turn to ${this.color} light`)
context.setState(this)
}
}
class Context {
constructor() {
this.state = null
}
// 获取状态
getState() {
return this.state
}
setState(state) {
this.state = state
}
}
const context = new Context()
const green = new State('green')
const yellow = new State('yellow')
const red = new State('red')
green.handle(context)
console.log(context.getState())
yellow.handle(context)
console.log(context.getState())
red.handle(context)
console.log(context.getState())
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。