js设计模式笔记小结

}

let car = new Kuaiche(100, ‘桑塔纳’)

let trip = new Trip(car)

trip.start()

trip.end()

02

image

// 车

class Car {

constructor(num) {

this.num = num

}

}

// 入口摄像头

class Camera {

shot(car) {

return {

num: car.num,

inTime: Date.now()

}

}

}

// 出口显示器

class Screen {

show(car, inTime) {

console.log(‘车牌号’, car.num)

console.log(‘停车时间’, Date.now() - inTime)

}

}

// 停车场

class Park {

constructor(floors) {

this.floors = floors || []

this.camera = new Camera()

this.screen = new Screen()

this.carList = {}

} in (car) {

// 获取摄像头的信息:号码 时间

const info = this.camera.shot(car)

// 停到某个车位

const i = parseInt(Math.random() * 100 % 100)

const place = this.floors[0].places[i]

place.in()

info.place = place

// 记录信息

this.carList[car.num] = info

}

out(car) {

// 获取信息

const info = this.carList[car.num]

const place = info.place

place.out()

// 显示时间

this.screen.show(car, info.inTime)

// 删除信息存储

delete this.carList[car.num]

}

emptyNum() {

return this.floors.map(floor => {

return ${floor.index} 层还有 ${floor.emptyPlaceNum()} 个车位

}).join(‘\n’)

}

}

// 层

class Floor {

constructor(index, places) {

this.index = index

this.places = places || []

}

emptyPlaceNum() {

let num = 0

this.places.forEach(p => {

if (p.empty) {

num = num + 1

}

})

return num

}

}

// 车位

class Place {

constructor() {

this.empty = true

} in () {

this.empty = false

}

out() {

this.empty = true

}

}

// 测试代码------------------------------

// 初始化停车场

const floors = []

for (let i = 0; i < 3; i++) {

const places = []

for (let j = 0; j < 100; j++) {

places[j] = new Place()

}

floors[i] = new Floor(i + 1, places)

}

const park = new Park(floors)

// 初始化车辆

const car1 = new Car(‘A1’)

const car2 = new Car(‘A2’)

const car3 = new Car(‘A3’)

console.log(‘第一辆车进入’)

console.log(park.emptyNum())

park.in(car1)

console.log(‘第二辆车进入’)

console.log(park.emptyNum())

park.in(car2)

console.log(‘第一辆车离开’)

park.out(car1)

console.log(‘第二辆车离开’)

park.out(car2)

console.log(‘第三辆车进入’)

console.log(park.emptyNum())

park.in(car3)

console.log(‘第三辆车离开’)

park.out(car3)

工厂模式


  • 介绍

  • UML && 代码演示

  • 使用场景

介绍:

  • 将 new 操作单独封装

  • 遇到 new 时,就要考虑是否该用工厂模式

class Product {

constructor(name) {

this.name = name

}

init() {

}

fun1() {

}

}

class Creator {

create(name) {

return new Product(name)

}

}

// test

let creator = new Creator()

let p1 = creator.create(‘p1’)

p1.init()

场景:

  • jQuery:$(‘div’)

  • $(‘div’) 和 new $(‘div’) 有何区别(查看上述 jQuery 例子)

  • React.createElement

  • vue 异步组件

学习经典库的方法:

  • 学习功能如何实现

  • 学习实现思路?怎么设计的

  • 强制自己写代码,模拟(刻意练习)

  • 模仿练习(拿来主义)

设计原则验证:

  • 构造函数和创建者分离

  • 符合开放封闭原则

单例模式


介绍:

  • 系统中被唯一使用的

  • 一个类只有一个实例。

登陆框、购物车…

说明:

  • 单例模式需要用到 java 的特性(private)

  • ES6 中没有(typescript 除外)

  • 只能用 java 代码来演示 UML 图的内容

场景:

  • jquery 只有一个 $

  • 模拟登陆框

  • 购物车

  • vuex 和 redux 中的 store

jQuery

// jQuery 只有一个 $

if (window.jQuery != null) {

return window.jQuery

} else {

// init

}

class SingleObject {

login() {

console.log(‘login…’)

}

}

SingleObject.getInstance = (function() {

let instance

return function() {

if (!instance) {

instance = new SingleObject();

}

return instance

}

})()

// 测试

let obj1 = SingleObject.getInstance()

obj1.login()

let obj2 = SingleObject.getInstance()

obj2.login()

console.log(obj1 === obj2)

class LoginForm {

constructor() {

this.state = ‘hide’

}

show() {

if (this.state === ‘show’) {

console.log(‘已经显示’)

return

}

this.state = ‘show’

console.log(‘登录框已显示’)

}

hide() {

if (this.state === ‘hide’) {

console.log(‘已经隐藏’)

return

}

this.state = ‘hide’

console.log(‘登录框已隐藏’)

}

}

LoginForm.getInstance = (function() {

let instance

return function() {

if (!instance) {

instance = new LoginForm();

}

return instance

}

})()

// 一个页面中调用登录框

let login1 = LoginForm.getInstance()

login1.show()

// login1.hide()

// 另一个页面中调用登录框

let login2 = LoginForm.getInstance()

login2.show()

// 两者是否相等

console.log(‘login1 === login2’, login1 === login2)

设计原则验证:

  • 符合单一职责原则,只实例化唯一的对象

  • 没法具体开放封闭原则,但是绝对不违反开放封闭原则

适配器模式


插头,转接口…

介绍:

  • 旧接口格式和使用者不兼容

  • 中间加一个适配转换接口

UML类图:

演示:

class Adaptee {

specificRequest() {

return ‘德国标准插头’

}

}

class Target {

constructor() {

this.adaptee = new Adaptee()

}

request() {

let info = this.adaptee.specificRequest()

return ${info} - 转换器 - 中国标准插头

}

}

// 测试代码

let target = new Target()

let res = target.request()

console.log(res)

场景:

  • 封装旧接口

  • vue computed

// 当前ajax封装

ajax({

url: ‘getData’,

type: ‘post’,

dataType: ‘json’,

data: {

id: “123”

}

}).done(function(){})

// 历史代码

// $.ajax({…})

// 做一层适配器

var $ = {

ajax: function(options) {

return ajax(options)

}

}

设计原则验证:

  • 将旧接口和使用者进行分离

  • 符合开放封闭原则

装饰器模式


介绍:

  • 为对象添加新功能

  • 不改变其原有的结构和功能

例子:手机壳

UML类图:

ES7 装饰器

babel插件:babel-plugin-transform-decorators-legacy

库:core-decorators(常用装饰器已经写好了,直接拿来用)

// 装饰类

// @testable

// class MyTestableClass {

// // …

// }

// function testable(target) {

// target.isTestable = true;

// }

// console.log(MyTestableClass.isTestable) // true

function mixins(…list) {

return function (target) {

Object.assign(target.prototype, …list)

}

}

const Foo = {

foo() { console.log(‘foo’) }

}

@mixins(Foo)

class MyClass {}

let obj = new MyClass();

obj.foo() // ‘foo’

// 装饰方法

function readonly(target, name, descriptor) {

// descriptor对象原来的值如下

// {

// value: specifiedFunction,

// enumerable: false,

// configurable: true,

// writable: true

// };

descriptor.writable = false;

return descriptor;

}

class Person {

constructor() {

this.first = ‘A’

this.last = ‘B’

}

@readonly

name() {

return ${this.first} ${this.last} }

}

var p = new Person()

console.log(p.name())

p.name = function() {} // 这里会报错,因为 name 是只读属性

// another

function log(target, name, descriptor) {

var oldValue = descriptor.value;

descriptor.value = function() {

console.log(Calling ${name} with, arguments);

return oldValue.apply(this, arguments);

};

return descriptor;

}

class Math {

@log

add(a, b) {

return a + b;

}

}

const math = new Math();

const result = math.add(2, 4);

console.log(‘result’, result);

// core-decorators

// import { readonly } from ‘core-decorators’

// class Person {

// @readonly

// name() {

// return ‘zhang’

// }

// }

// let p = new Person()

// console.log(p.name())

// // p.name = function () { // } // 此处会报错

import { deprecate } from ‘core-decorators’;

class Person {

@deprecate

facepalm() {}

@deprecate(‘We stopped facepalming’)

facepalmHard() {}

@deprecate(‘We stopped facepalming’, { url: ‘http://knowyourmeme.com/memes/facepalm’ })

facepalmHarder() {}

}

let person = new Person();

person.facepalm();

// DEPRECATION Person#facepalm: This function will be removed in future versions.

person.facepalmHard();

// DEPRECATION Person#facepalmHard: We stopped facepalming

person.facepalmHarder();

// DEPRECATION Person#facepalmHarder: We stopped facepalming

//

// See http://knowyourmeme.com/memes/facepalm for more details.

设计原则验证:

  • 将现有对象和装饰器进行分离,两者独立存在

  • 符合开放封闭原则

代理模式


介绍:

  • 使用者无权访问目标对象

  • 中间加代理,通过代理做授权和控制

例子:

  • 科学上网

  • 明星经纪人

UML类图:

演示:

场景:

  • 网页事件代理

  • jQuery $.proxy

  • ES6 Proxy

// 明星

let star = {

name: ‘张XX’,

age: 25,

phone: ‘13910733521’

}

// 经纪人

let agent = new Proxy(star, {

get: function(target, key) {

if (key === ‘phone’) {

// 返回经纪人自己的手机号

return ‘18611112222’

}

if (key === ‘price’) {

// 明星不报价,经纪人报价

return 120000

}

return target[key]

},

set: function(target, key, val) {

if (key === ‘customPrice’) {

if (val < 100000) {

// 最低 10w

throw new Error(‘价格太低’)

} else {

target[key] = val

return true

}

}

}

})

// 主办方

console.log(agent.name)

console.log(agent.age)

console.log(agent.phone)

console.log(agent.price)

// 想自己提供报价(砍价,或者高价争抢)

agent.customPrice = 150000

// agent.customPrice = 90000 // 报错:价格太低

console.log(‘customPrice’, agent.customPrice)

访问代理,接口地址是不会变的。

设计原则验证:

  • 代理类和目标类分离,隔离开目标类和使用者

  • 符合开放封闭原则

代理模式 VS 适配器模式

  • 适配器模式:提供一个不同的接口

  • 代理模式:提供一模一样的接口

代理模式 VS 装饰器模式

  • 装饰器模式:扩展功能,原有功能不变且可直接使用

  • 代理模式:显示原有功能,但是经过限制或者阉割之后的

外观模式


介绍:

  • 为子系统中的一组接口提供了一个高层接口

  • 使用者使用这个高层接口

观察者模式


介绍:

  • 发布 & 订阅

  • 一对多

UML类图:

例子:

  • 点咖啡,点好后坐等被叫

// 主题,接收状态变化,触发每个观察者

class Subject {

constructor() {

this.state = 0

this.observers = []

}

getState() {

return this.state

}

setState(state) {

this.state = state

this.notifyAllObservers()

}

attach(observer) {

this.observers.push(observer)

}

notifyAllObservers() {

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: ${this.subject.getState()})

}

}

// 测试代码

let s = new Subject()

let o1 = new Observer(‘o1’, s)

let o2 = new Observer(‘o2’, s)

let o3 = new Observer(‘o3’, s)

s.setState(1)

s.setState(2)

s.setState(3)

场景:

  • 网页事件绑定

  • Promise

  • jQuery callbacks

  • nodeJs 自定义事件

// jquery callbacks

var callbacks = $.Callbacks() // 注意大小写

callbacks.add(function(info) {

console.log(‘fn1’, info)

})

callbacks.add(function(info) {

console.log(‘fn2’, info)

})

callbacks.add(function(info) {

console.log(‘fn3’, info)

})

callbacks.fire(‘gogogo’) // 发布

callbacks.fire(‘fire’)

// nodejs 自定义事件

const EventEmitter = require(‘events’).EventEmitter

const emitter1 = new EventEmitter()

emitter1.on(‘some’, () => {

// 监听 some 事件

console.log(‘some event is occured 1’)

})

emitter1.on(‘some’, () => {

// 监听 some 事件

console.log(‘some event is occured 2’)

})

// 触发 some 事件

emitter1.emit(‘some’)

const emitter = new EventEmitter()

emitter.on(‘sbowName’, name => {

console.log('event occured ', name)

})

emitter.emit(‘sbowName’, ‘zhangsan’) // emit 时候可以传递参数过去


// 任何构造函数都可以继承 EventEmitter 的方法 on emit

class Dog extends EventEmitter {

constructor(name) {

super()

this.name = name

}

}

var simon = new Dog(‘simon’)

simon.on(‘bark’, function() {

console.log(this.name, ’ barked’)

})

setInterval(() => {

simon.emit(‘bark’)

}, 500)

node Stream 用到了自定义事件:

// 文件太大,用流的形式读取

// 监听文件有多少字符

var fs = require(‘fs’)

var readStream = fs.createReadStream(‘./data/file1.txt’) // 读取文件的 Stream

var length = 0

readStream.on(‘data’, function(chunk) {

length += chunk.toString().length

})

readStream.on(‘end’, function() {

console.log(length)

})

// 监听一行一行的数据(监听文件有多少行)

var readline = require(‘readline’);

var fs = require(‘fs’)

var rl = readline.createInterface({

input: fs.createReadStream(‘./data/file1.txt’)

});

var lineNum = 0

rl.on(‘line’, function(line) {

lineNum++

});

rl.on(‘close’, function() {

console.log(‘lineNum’, lineNum)

});

其他场景:

  • nodeJs 中: 处理 http 请求;多进程通讯

  • vue 和 react 组件生命周期触发

  • vue watch

// node 处理 http 请求

var http = require(‘http’)

function serverCallback(req, res) {

var method = req.method.toLowerCase() // 获取请求的方法

if (method === ‘get’) {}

if (method === ‘post’) {

// 接收 post 请求的内容

var data = ‘’

req.on(‘data’, function(chunk) {

// “一点一点”接收内容

console.log(‘chunk’, chunk.toString())

data += chunk.toString()

})

req.on(‘end’, function() {

// 接收完毕,将内容输出

console.log(‘end’)

res.writeHead(200, { ‘Content-type’: ‘text/html’ })

res.write(data)

res.end()

})

}

}

http.createServer(serverCallback).listen(8081) // 注意端口别和其他 server 的冲突

console.log(‘监听 8081 端口……’)

设计原则验证:

  • 主题和观察者分离,不是主动触发而是被动监听,两者解耦

  • 符合开放封闭原则

迭代器模式


介绍:

  • 顺序访问一个集合(有序集合)

  • 使用者无需知道集合的内部结构(封装)

UML类图:

使用 jQuery 示例:

var arr = [1, 2, 3]

var nodeList = document.getElementsByTagName(‘p’)

var $p = $(‘p’)

// 要对这三个变量进行遍历,需要写三个遍历方法

// 第一

arr.forEach(function(item) {

console.log(item)

})

// 第二

var i, length = nodeList.length

for (i = 0; i < length; i++) {

console.log(nodeList[i])

}

// 第三

$p.each(function(key, p) {

console.log(key, p)

})

// 如何能写出一个方法来遍历这三个对象呢

function each(data) {

var $data = $(data) // 生成迭代器

$data.each(function(key, p) {

console.log(key, p)

})

}

each(arr)

each(nodeList)

each($p)

class Iterator {

constructor(conatiner) {

this.list = conatiner.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

}

}

class Container {

constructor(list) {

this.list = list

}

getIterator() {

return new Iterator(this)

}

}

// 测试代码

let container = new Container([1, 2, 3, 4, 5])

let iterator = container.getIterator()

while (iterator.hasNext()) {

console.log(iterator.next())

}

使用场景:

  • JQuery each

  • ES6 Iterator

为何 ES6 Iterator 存在?

  • ES6 语法中,有序集合的数据类型已经很多

  • Array Map Set String TypedArray arguments NodeList

  • 需要有一个统一的遍历接口来遍历所有数据类型

  • 注意:object 不是有序集合,可以用 Map 代替

ES6 Iterator 是什么?

  • 以上数据类型,都有 [Symbol.iterator] 属性

  • 属性值是函数,执行函数返回一个迭代器

  • 这个迭代器就有 next 方法可顺序迭代子元素

  • 可运行 Array.prototype[Symbol.iterator] 来测试

ES6 Iterator 示例:

// 手动实现遍历器

function each(data) {

// 生成遍历器

let iterator = dataSymbol.iterator

// console.log(iterator.next()) // 有数据时返回 {value: 1, done: false}

// console.log(iterator.next())

// console.log(iterator.next())

// console.log(iterator.next())

// console.log(iterator.next()) // 没有数据时返回 {value: undefined, done: true}

let item = { done: false }

while (!item.done) {

item = iterator.next()

if (!item.done) {

console.log(item.value)

}

}

}

// 数组已经部署了 Iterator 接口

// for of 可以遍历 所有部署了 Iterator 接口的数据结构

function each(data) {

for (let item of data) {

console.log(item)

}

}

let arr = [1, 2, 3, 4]

let nodeList = document.getElementsByTagName(‘p’)

let m = new Map()

m.set(‘a’, 100)

m.set(‘b’, 200)

each(arr)

each(nodeList)

each(m)

ES6 Iterator 与 Generator

  • Iterator 的价值不限于上述几个类型的遍历

  • 还有 Generator 函数的使用

  • 即只要返回的数据符合 Iterator 接口要求

  • 即可使用 Iterator 语法,这就是迭代器模式

// function* helloWorldGenerator() {

// yield ‘hello’;

// yield ‘world’;

// return ‘ending’;

// }

// var hw = helloWorldGenerator();

// console.log(hw.next())

// console.log(hw.next())

// console.log(hw.next())

// console.log(hw.next())

function* foo() {

yield 1;

yield 2;

yield 3;

yield 4;

yield 5;

return 6;

}

for (let v of foo()) {

console.log(v);

}

设计原则验证:

  • 迭代器对象和目标对象分离

  • 迭代器将使用者与目标对象分离开

  • 符合开放封闭原则

状态模式


介绍:

  • 一个对象有状态的变化

  • 每次状态变化都会触发一个逻辑

  • 不能总用 if…else 来控制

UML类图:

// 状态

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

}

setState(state) {

this.state = state

}

getState() {

return this.state

}

}

// 测试代码

let context = new Context()

let greed = new State(‘greed’)

let yellow = new State(‘yellow’)

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
3, 4]

let nodeList = document.getElementsByTagName(‘p’)

let m = new Map()

m.set(‘a’, 100)

m.set(‘b’, 200)

each(arr)

each(nodeList)

each(m)

ES6 Iterator 与 Generator

  • Iterator 的价值不限于上述几个类型的遍历

  • 还有 Generator 函数的使用

  • 即只要返回的数据符合 Iterator 接口要求

  • 即可使用 Iterator 语法,这就是迭代器模式

// function* helloWorldGenerator() {

// yield ‘hello’;

// yield ‘world’;

// return ‘ending’;

// }

// var hw = helloWorldGenerator();

// console.log(hw.next())

// console.log(hw.next())

// console.log(hw.next())

// console.log(hw.next())

function* foo() {

yield 1;

yield 2;

yield 3;

yield 4;

yield 5;

return 6;

}

for (let v of foo()) {

console.log(v);

}

设计原则验证:

  • 迭代器对象和目标对象分离

  • 迭代器将使用者与目标对象分离开

  • 符合开放封闭原则

状态模式


介绍:

  • 一个对象有状态的变化

  • 每次状态变化都会触发一个逻辑

  • 不能总用 if…else 来控制

UML类图:

// 状态

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

}

setState(state) {

this.state = state

}

getState() {

return this.state

}

}

// 测试代码

let context = new Context()

let greed = new State(‘greed’)

let yellow = new State(‘yellow’)

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-oS0DxrxZ-1715548257829)]

[外链图片转存中…(img-ThNQ9cuA-1715548257829)]

[外链图片转存中…(img-BlQZ0Rfa-1715548257830)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值