前端常用设计模式总结 和 练习
面向对象
类 - 模板
class People {
constructor(name, age) {
this.name = name;
this.age = age
}
eat() {
console.log(`${ this.name } eat something`)
}
read() {
console.log(`${ this.name } reading book`)
}
}
let zhangsan = new People('zhangsan', 20);
zhangsan.eat()
let wang = new People('wang', 19);
wang.read()
三要素
一 、继承 子类继承父类
class Student extends People {
constructor(name, age, number) {
super(name, age)
this.number = number
}
study() {
console.log(`${ this.name } can study`)
}
}
let xiaohong = new Student('xiaohong', 12, 234)
xiaohong.study()
xiaohong.rend()
xiaohong.eat()
二 、封装 数据的权限和保密
* 减少耦合,不该外露的不外露
* 利于数据、接口的权限管理
* ES6 目前不支持 一般认为 _ 开头的属性是private的
class People {
age
name
protected weight
constructor(name, age) {
this.name = name
this.age = age
this.weight = 120
}
eat() {
console.log(`${ this.name } eat something`)
}
read() {
console.log(`${ this.name } reading book`)
}
}
class Student extends People {
number
private girlfriend
constructor(name, age, number) {
super(name, age)
this.number = number
this.girlfriend = 'xiaobai'
}
study() {
console.log(`${ this.name } can study`)
}
getWeight() {
console.log(`${ this.weight }`)
}
}
三 、多态 同一接口不同实现
* 同一个接口 不同表现
* JS 应用极少
* 需要结合java等语言的接口、重写、重载等功能
配置开发环境
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: path.join(__dirname, './src/01/index.js'),
test: path.join(__dirname, './src/02/index.js')
},
output: {
path: __dirname,
filename: './js/[name].js'
},
devServer: {
contentBase: './',
compress: true,
port: 8888,
host: 'localhost',
open: true
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /(node_modules)/,
loader: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, 'public/index.html')
})
]
}
{
"presets" ["es2015", "latest"],
"plugins": ["transform-decorators-legacy"]
}
常用设计模式
工厂、单例、适配器、装饰器、代理、外观、观察者
console.log('hello world')
class MyPromise {
constructor() {}
then() {}
resolve() {}
reject() {}
}
function loadimg (src) {
let promise = new Promise((resolve, reject) =>{
let img = document.createElement('img')
img.onload(()=> {
resolve(img)
})
img.onerror(() => {
reject('error')
})
img.src = src
})
return promise
}
console.log(new MyPromise())
class Product {
constructor(name) {
this.name = name
}
init() {
console.log('init')
}
fn1() {
console.log('fn1')
}
fn2() {}
}
class Creator {
create(name) {
return new Product(name)
}
}
let creator = new Creator()
let prod = creator.create(name);
prod.init()
class SingleObject {
login(userName, password) {}
}
SingleObject.getInstance = (function(){
let instance
return function () {
if(!instance) {
instance = new SingleObject()
}
return instance
}
})()
let obj1 = SingleObject.getInstance()
let obj2 = SingleObject.getInstance()
console.log('obj1 === obj2 ', obj1 === obj2)
class LoginForm {
constructor() {
this.state = 'hide'
}
show() {
if (this.state === 'hide') {
this.state = 'show'
console.log('show login')
} else {
console.log('already show')
}
}
hide() {
if (this.state === 'show') {
this.state = 'hide'
console.log('login hide')
} else {
console.log('already hide')
}
}
}
LoginForm.getInstance = (function() {
let instance
return function() {
if (!instance) {
instance = new LoginForm()
}
return instance
}
})()
let login1 = LoginForm.getInstance()
let login2 = LoginForm.getInstance()
console.log('login1 === login2', login1 === login2)
class Adatee {
specificRequest() {
return '德国标准插头'
}
}
class Target {
constructor() {
this.adatee = new Adatee()
}
request() {
let info = this.adatee.specificRequest()
return `${info} - 转换器 - 中国标准插头`
}
}
let target = new Target()
console.log(target.request())
class Circle {
draw() {
console.log('华画圆')
}
}
class Decorator {
constructor(circle) {
this.circle = circle
}
draw() {
this.circle.draw()
this.setBorderRed(this.circle)
}
setBorderRed() {
console.log('draw red border')
}
}
let circle = new Circle()
circle.draw()
let decorator = new Decorator(circle)
decorator.draw()
@mydDecorator
class A {}
class As {}
function mydDecorator(target) {
}
@testDec
class Demo {}
function testDec (target) {
target.isDev = true
}
console.log('test decorator', Demo.isDev)
function testParam(isDev) {
return function(target) {
target.isDev = isDev
}
}
@testParam('aaa')
class Demmo {}
console.log('测试带参数 ', Demmo.isDev)
function mixins(...list) {
return function(target) {
Object.assign(target.prototype, ...list)
}
}
const Foo = {
foo () {
console.log('foo')
}
}
@mixins(Foo)
class HaClass {
}
let obj = new HaClass()
obj.foo()
function readyonly(target, name, descriptor) {
descriptor.writable = false
return descriptor
}
class Person {
constructor() {
this.first = 'Q'
this.last = 'M'
}
@readyonly
name() {
return `${this.first} ${this.last}`
}
}
let mp = new Person()
console.log(mp.name())
function log (target, name, descriptor) {
let oldValue = descriptor.value
descriptor.value = function () {
console.log(`Calling ${name} with`, arguments)
return oldValue.apply(this, arguments)
}
return descriptor
}
class MyMath {
@log
add(a,b) {
return a + b
}
}
const math = new MyMath()
const resu = math.add(2, 4)
console.log('resukt - ', resu)
class RealImg {
constructor (filename) {
this.filename = filename
this.loadFileFromDisk()
}
loadFileFromDisk() {}
display() {
console.log(`${this.filename}`)
}
}
class ProxyImg {
constructor(filename) {
this.realImg = new RealImg(filename)
}
display() {
this.realImg.display()
}
}
let proxyImg = new ProxyImg('my name')
proxyImg.display()
let Star = {
name: '张翰',
age: 25,
phone: 13234234948
}
let agent = new Proxy(Star, {
get: function(target, key) {
if (key === 'phone') {
return '10010'
}
if (key === 'price') {
return '15万'
}
return target[key]
},
set: function(target, key, value) {
if (key === 'customPrice') {
if (value < 100000) {
throw new Error('报价太低')
} else {
target[key] = value
return true
}
}
}
})
class Subject {
constructor() {
this.state = 0
this.observers = []
}
getState() {
return this.state
}
setState(state) {
this.state = state
this.notifyAllObservers()
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update()
})
}
attach(observer) {
this.observers.push(observer)
}
}
class Observer {
constructor(name, subject) {
this.name = name
this.subject = subject
this.subject.attach(this)
}
update() {
console.log(`${this.name } update state ${this.subject.getState()}`)
}
}
let sub = new Subject();
let observer1 = new Observer('01', sub)
let observer2 = new Observer('02', sub)
let observer3 = new Observer('03', sub)
sub.setState(1)
sub.setState(2)
迭代器模式和状态机模式
import StateMachine from 'javascript-state-machine';
import $ from 'jquery';
// 迭代器模式
// 特点 : 1. 顺序访问一个集合 2. 使用者无需知道集合内部的结构(数据被封装过)
// 案例:$.each array.forEach
function each(data) {
var $data = $(data) // 生成迭代器
$data.each(function(key, val) {
console.log(key, val)
})
}
// 使用者不需要知道集合的内部结构
/**
*
* Container { list: Array getIterator(): Iterator }
*
* Iterator { list: Array index:int next():int hasNext():boolean }
*
* next() ---> 数据遍历
*
* hasNext() ----> 是否有下一个节点
*
*/
class Continer {
constructor(list) {
this.list = list
}
// 生成遍历器
getIterator(){
return new Iterator(this)
}
}
class Iterator {
constructor (container) {
this.list = container.list
this.index = 0
}
next () {
if (this.hasNext()) {
return this.list[this.index++]
}
return null
}
hasNext() {
if (this.index >= this.list.length) {
return false
}
return true
}
}
// test
var arr = [1,2,3,4,5,6]
let containerr = new Continer(arr)
let iterator = containerr.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}
// 使用场景
/**
*
* 1. jQuery each
*
* 2. ES6 Iterator 为何存在 实现了迭代器模式 有序集合的类型已经有很多了,统一一种方式去遍历
* Array Map Set String TypedArray arguments NodeList
* Object 不是有序集合
*
* 以上的数据类型都有 [Symbol.iterator]属性
* 属性值是函数,执行函数返回一个迭代器 next()方法顺序迭代子元素
* Array.prototype[Symbol.iterator]
* Array.prototype[Symbol.iterator]() -> Array Iterator {}
* Array.prototype[Symbol.iterator]().next() -> { value: undefined, done: true }
* 并不是没一个人都会去使用 [Symbol.iterator] 所以 会有了 for ... of 他其实也是用的 iterator 去进行的迭代
*
* 3.
*
*/
function myEach(data) {
if (data[Symbol.iterator]) {
var it = data[Symbol.iterator]()
let item = {done: false}
while(!item.done) {
item = it.next()
console.log(item.value)
}
// 必须有 [Symbol.iterator]
// for (let item of data) {
// console.log(item)
// }
} else {
throw new Error('该数据不存在迭代器')
}
}
var aArr = [1,2,3,4,5,6]
myEach(aArr)
let maps = new Map()
maps.set('a', 120)
maps.set('b', 190)
myEach(maps)
/**
* Iterator 的价值不限于上述几种类型
* 还有 Generator
* function* helloWorld() {
* yield 'hello';
* yield 'hello';
* return 'ending'
* }
* var wph = helloWorld();
* wph.next();
* wph.next();
* wph.next();
* wph.next();
*/
// 总结: 迭代器对象和目标对象分离 迭代器将使用者隔离开 符合开放封闭原则
// 状态模式
/**
*
* 一个对象有状态变化
*
* 每次状态变化都触发一个逻辑
*
* 不能总是 if else 控制
*
* 示例: 红绿灯变化
*
* State { color handle(context) }
*
* Context { state getState(): state setState(state):void }
*
*/
// 红灯 绿灯 黄灯
class State {
constructor(color) {
this.color = color
}
handle(context) {
console.log('turn to ' + this.color)
context.setState(this)
}
}
// 主体
class Context {
constructor(){
this.state = null
}
getState() {
return this.state
}
setState(state) {
this.state = state
}
}
// test
let context = new Context()
let red = new State('red')
let green = new State('green')
let yellow = new State('yellow')
// 绿灯亮
green.handle(context)
context.getState()
// 红灯亮
red.handle(context)
context.getState()
// 黄灯亮
yellow.handle(context)
context.getState()
/**
*
* 有限状态机 javascript-state-machine 库
* 有限个状态、以及在这些状态之间变化 -- 交通灯
*
* 简单的 Promise 实现
*
*/
let fsm = new StateMachine({
init: '收藏',
transitions: [
{
name: 'doStore',
from: '收藏',
to: '取消收藏'
},
{
name: 'delStore',
from: '取消收藏',
to: '收藏'
},
],
methods:{
onDoStore: function() {
updateText()
},
onDelStore: function() {
updateText()
}
}
})
let btn = $('#btn')
btn.click(function() {
if (fsm.is('收藏')) {
fsm.doStore()
} else {
fsm.delStore()
}
})
function updateText() {
btn.text(fsm.state)
}
// init text
updateText()
// 简单的 Promise
/**
* 三种状态 pending fullfilled rejected z状态不可逆
*/
var mfsm = new StateMachine({
init: 'pending',
transitions: [
{
name: 'resolve',
from: 'pending',
to: 'fullfilled'
},
{
name:'reject',
from: 'pending',
to:'rejected'
}
],
methods: {
onResolve: function(state, data) {
data.successList.forEach(fn => fn())
},
onReject: function( state, data) {
data.failList.forEach(fn => fn())
}
}
})
class TPromise {
constructor(fn) {
this.successList = []
this.failList = []
fn(() => {
mfsm.resolve(this)
},() => {
mfsm.reject(this)
})
}
then(successFn,failFn) {
this.successList.push(successFn)
this.failList.push(failFn)
}
}
// test
function loadImage(src) {
const prom = new TPromise(function(resolve,reject) {
let img = document.createElement('img')
img.onload = () => {
resolve(img)
}
img.onerror = () => {
reject()
}
img.src = src
})
return prom
}
let srcs = 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1593106255,4245861836&fm=26&gp=0.jpg'
let resss = loadImage(srcs)
resss.then(function() {
// success
console.log('ok')
}, function() {
//fail
console.log('fail')
})
// 状态变化