下一个泡泡玛特工程师是谁呢?

收集一些自己用到的prompt

2023-04-05 · 1 min read

机械键盘相关参数

机械键盘相关参数记录

2023-04-05 · 1 min read

Web 前端性能优化

常用的web前端性能优化手段

2023-03-19 · 3 min read

Blender常用快捷键

自己经常用到的一些快捷键记录

2022-12-11 · 1 min read

Docker 常用命令

精简 Docker 常用命令

2022-11-06 · 1 min read

项目管理

项目管理相关知识

2022-11-06 · 1 min read

JS沙箱sandbox的各种实现

我们把Js隔离机制常常称作沙箱

2022-09-18 · 1 min read

Amazing!Solid 比react还react

今天来介绍2个amazing的东东

2022-09-12 · 1 min read

puppeteer应用

使用傀儡师来操作浏览器这个傀儡吧

2022-09-11 · 1 min read

做一个web termianl

前端react, 后端nodejs, 直接可用版web termianl

2022-07-17 · 1 min read

TypeScript里常用的工具类型

经常用到的工具类型,提取抽离出来,供以后复用

2022-06-26 · 1 min read

Vim大法好

想要丢掉鼠标,试试Vim

2022-06-26 · 1 min read

碧血丹心

无论时光如何沾染风霜,也永似红日光

2022-05-10 · 1 min read

chrome V8 引擎中的垃圾回收机制

V8引擎就是nodejs的发动机

2022-03-03 · 1 min read

如何开发一个cli

命令行交互界面是程序员必备的工具,如何开发一个呢?

2022-02-21 · 1 min read

Javascript中的哲学

道生一,一生二,二生三,三生万物

2022-02-20 · 1 min read

React Redux 实现 (Context 版)

React Redux 实现 (Context 版)

2022-02-16 · 1 min read

mobx-react 使用

虽然不常用,但是可以学一下

2022-02-16 · 1 min read

Less中的for和forEach循环

循环的使用是保持代码干燥和避免重复的好方法

2022-02-15 · 1 min read

Webpack Splitchunks 详解

webpack 优化

2022-02-15 · 1 min read

ssh-keygen命令详解

为ssh生成、管理和转换认证密钥

2022-02-15 · 1 min read

使用CURL发送POST请求

curl 是常用的命令行工具,用来请求 Web 服务器。

2022-02-15 · 1 min read

Webpack Plugin 开发

让我们来学一下如何开发一个webpack插件

2022-02-14 · 1 min read

JS 实现两个大数相加?

algo-adding-large-numbers

2022-02-10 · 1 min read

字典树 trie

字典树 trie

2022-02-09 · 1 min read

浏览器原理问题

浏览器原理问题

2022-01-11 · 1 min read

MacBook快速进入一个文件夹目录

mac如何快速进入一个文件夹

2021-10-26 · 1 min read

react合成事件

react-synthetic-event

2021-10-25 · 1 min read

mini webpack实现

通过babel核心来实现迷你版的webpack

2021-10-10 · 1 min read

设计模式

在程序设计中有很多实用的设计模式,而其中大部分语言的实现都是基于类

2021-10-10 · 1 min read

babel核心

babel核心介绍

2021-10-07 · 1 min read

React 15 和 React 16 的区别

react-15-16

2021-10-06 · 1 min read

React性能优化

浅谈react性能优化的方法

2021-10-05 · 1 min read

交通信号灯实现

如何用js来实现交通信号灯呢

2021-09-25 · 1 min read

内存管理

前端中的内存管理

2021-09-25 · 1 min read

前端安全

前端关于安全方面的知识

2021-09-25 · 2 min read

网络和并发

http各版本对于并发的支持,前端如何控制并发量?

2021-09-25 · 1 min read

跨域方法

总结了9种跨域方法

2021-09-24 · 1 min read

react virtualList 虚拟列表无限滚动实现

用react实现虚拟滚动

2021-09-16 · 1 min read

监控埋点方案

前端监控埋点方案

2021-09-16 · 1 min read

Mini useEffect实现

如何实现useEffect?

2021-09-11 · 1 min read

Mini useState 实现

我们来思考一下useState是怎么实现的呢?

2021-09-11 · 1 min read

React Fiber

react-fiber

2021-09-11 · 1 min read

React class组件和function组件异同

类组件和函数组件有何相同点有何不同点呢?

2021-09-11 · 1 min read

Lodash Get 实现

algo-lodash-get

2021-09-08 · 1 min read

手写reduce实现

algo-reduce

2021-09-08 · 1 min read

Hooks 原理概览

react-hooks

2021-09-05 · 1 min read

Hook原理——状态Hook

react-hook-state

2021-09-05 · 1 min read

手写Mini Redux实现

手写一个简易版的redux实现,包含了核心逻辑

2021-09-04 · 1 min read

legacy和concurrent模式

react-legacy-concurrent

2021-09-01 · 1 min read

react架构

总体 react 的核心可以用 ui=fn(state)来表示 3 大核心对象、3 大核心阶段、2 大工作循环 Scheduler(调度器): 排序优先级,让优先级高的任务先进行 reconcile Reconciler…

2021-09-01 · 2 min read

react核心api和jsx

为什么要有jsx,为什么会有虚拟bom

2021-09-01 · 1 min read

setState是同步的还是异步的

react-setstate-usestate

2021-08-31 · 1 min read

如何使用NodeJs创建HTTP服务?

如何使用NodeJs创建HTTP服务?

2021-08-17 · 1 min read

NodeJS 事件循环模型

nodejs-eventloop

2021-08-11 · 2 min read

Buffer

nodejs中的内存管理

2021-08-10 · 1 min read

微前端解决方案-qiankun

目前国内最好的微前端解决方案-qiankun

2021-08-10 · 1 min read

React Mini版实现(1)

学一门技术最好的方法就是做一个其玩具版的实现,我们来尝试实现一下react和react-dom最简单版本吧

2021-08-04 · 1 min read

brew安装

brew 是 MacOS 上的包管理工具,可以简化 macOS 和 Linux 操作系统上软件的安装。

2021-08-04 · 1 min read

CommonJS简易版实现

CommonJS我们经常用,如何实现一个简易版的commonJS呢?

2021-08-01 · 1 min read

极品透明Dashboard样式分享

一个极品透明Dashboard样式分享

2021-07-30 · 1 min read

Stream

nodejs中的流

2021-07-11 · 1 min read

NodeJS全局对象

JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

2021-05-18 · 1 min read

如何部署Nodejs服务

如何快速的部署一个Nodejs服务到公网呢?

2021-05-12 · 1 min read

Events

events模块是node的核心模块之一,几乎所有常用的node模块都继承了events模块,比如http、fs等。

2021-05-11 · 1 min read

用JS绘制背景,让CSS直接使用 'background:paint(xxx)'

CSS对象新特性,新的background设置方式

2021-04-30 · 1 min read

Block Formatting Context 块级格式化上下文

可以将BFC看成是元素的一种属性,拥有了这种属性的元素就会使他的子元素与世隔绝,不会影响到外部其他元素

2021-04-17 · 1 min read

如何使用Nodejs来创建一个TCP/UDP服务?

如何使用Nodejs来创建一个TCP/UDP服务?

2021-04-17 · 1 min read

Mac使用tree生成目录结构

程序员经常会有需求,需要列出项目的结构树。Mac或者Linux下可以使用tree列出项目结构

2021-04-04 · 1 min read

常见算法

一些比较常见算法

2021-03-20 · 1 min read

前端缓存

对于性能优化离不开缓存

2021-02-28 · 1 min read

前端答疑

一些比较常见的问题

2020-09-02 · 1 min read

Nodejs 网络 & HTTP

nodejs-network

2020-08-14 · 1 min read

JavaScript AST 抽象语法树

源代码的抽象语法结构的树状表现形式

2020-08-02 · 1 min read

nodejs里面向切面编程的一种范式

在一些场景下我们可能需要一种面向切面的编程方式

2020-08-01 · 1 min read

各种JS模块化特性

AMD、CMD、CJS、ESM

2020-05-30 · 1 min read

手写Ajax实现

使用HMR一步步实现Ajax

2020-05-30 · min read

手写PromiseA+实现

如何自己实现promiseA+规范,手写一个promise实现

2020-04-30 · 1 min read

debug和内存泄露

nodejs的debug方法

2020-04-08 · 1 min read

Javascript prototype 原型链

js-prototype

2019-09-07 · 1 min read

this指针、作用域

this是在执行时动态读取上下文决定的,不是在定义时决定

2019-06-14 · 1 min read

call、apply、bind的极简实现

使用symbol实

2019-06-03 · 1 min read

CSS联合选择器区分列表元素个数不同所要求的不同样式

对于列表,在有些时候针对于不同个数的item会有不同的显示,比如col份数,用js固然可以,是否可以用css更简便的实现呢?

2019-04-30 · 1 min read

TypeScript基础

介绍TypeScript基础知识

2019-04-30 · 1 min read

JS中的变量提升

为什么js当时要这样设计

2019-03-07 · 1 min read

ES6之Class

关于ES6里的class, 我们有什么不知道的事?

2019-03-01 · 1 min read

HTTP详解

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传输协议

2019-02-17 · 2 min read

极简代码实现节流Throttle和防抖Debounce

使用各9行代码实现节流和防抖函数

2019-01-30 · 1 min read

Javascript 事件循环 EventLoop

js-eventloop

2018-09-07 · 1 min read

POST和GET区别

POST和GET区别是什么呢?

2018-08-14 · 1 min read

正则表达式

正则表达式一锅端

2018-07-30 · 1 min read

cloneDeep 深克隆实现

algo-clonedeep

2017-09-07 · 1 min read

webpack

engin-webpack

2017-09-07 · 1 min read

http1.1和http2.0有什么区别

http1.1和http2.0有什么区别

2017-06-14 · 1 min read

手写EventEmitter事件巴士

咱们来手写实现一个EventEmitter事件巴士

2017-01-10 · 1 min read

OOP 面向对象编程

对象是什么?为什么要面向对象?

2016-05-08 · 1 min read
Stay hungry & Stay foolish
战歌
16
The Reluctant Warrior
Immediate Music
To Glory
Two Steps From Hell
Victory
Two Steps From Hell
Empire of Angels
Thomas Bergersen
Serenata Immortale
Immediate Music
Cornfield Chase
Hans Zimmer
Tennessee
Hans Zimmer
He's a Pirate
Martin Ermen
Rise
Hans Zimmer
On Thin Ice
Hans Zimmer
Angels Will Rise
Twisted Jukebo
When It All Falls Down
Audiomachine
Icarus
Ivan Torrent
Star Sky - Instrumental
Two Steps From Hell
亡灵序曲
L
Up Is Down
Hans Zimm
回到首页

设计模式

袁官东
October 10th, 2021 · 1 min read
图:Peter Tarka

1.Single Responsibility Principle,单一职责原则,简称SRP 实现类要职责单一。

2.Open Close Principle,开闭原则,简称OCP 对扩展开放,对修改关闭。

3.Liskov Substitution Principle,里氏替换原则,简称LSP 不要破坏继承体系。

4.Interface Segregation Principle,接口隔离原则,简称ISP 设计接口的时候要精简单一。

5.Dependence Inversion Principle,依赖倒置原则,简称DIP

设计原则

1、开放-封闭原则(OCP)

软件实体(类、模块、函数)等应该是可以 扩展的,但是不可修改

当需要改变一个程序的功能或者给这个程序增加新功能的时候,可以使用增加代码的方式,尽量避免改动程序的源代码,防止影响原系统的稳定

2、单一职责原则 (SRP)

不同的类具备不同的职责,各司其职。做系统设计是,如果发现有一个类拥有了两种职责,那么就要问一个问题:可以将这个类分成两个类吗?如果真的有必要,那就分开,千万不要让一个类干的事情太多。 如果一个方法承担了过多的职责,那么在需求的变迁过程中,需要改写这个方法的可能性就越大。

3、最少知道原则 (LKP 迪米特原则)

一个对象应该对其他对象有最少的了解, 类中只暴露不得不暴露的,其内部实现不暴露出去。

4、接口隔离原则

如果一个类实现一个接口,但这个接口中有它不需要的方法,那么就需要把这个接口拆分,把它需要的方法提取出来,组成一个新的接口让这个类去实现,一个接口对实现它的类都是有用的。接口足够小

5、依赖倒置原则

举例人吃苹果,我想吃苹果,但是我还想吃橘子,如果按照程序思维的话。就是三个类型,人 Class,苹果 Class,橘子 Class,这种方式冗杂不好维护,不易理解,用水果来抽象化,苹果类继承并实现吃的动作。 使用接口或抽象类 上层不应依赖下层实现

6、里氏替换原则

是对开闭原则的补充,子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法 子类中可以增加自己特有的方法

模式分类

模式分类名称
创建型工厂模式
单例模式
建造者模式
原型模式
结构型适配器模式
装饰器模式
代理模式
行为型策略模式
迭代器模式
观察者模式
命令模式
状态模式
模板模式
职责链模式
享元模式

工厂模式

工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象,用工厂方法代替 new 操作的一种模式。 构造函数和创建者分离,对 new 操作进行封装,隐藏创建过程、暴露共同接口, 符合开放封闭原则

1class Creator {
2 create(name) {
3 return new Animal(name);
4 }
5}
6
7class Animal {
8 constructor(name) {
9 this.name = name;
10 }
11}
12
13var creator = new Creator();
14
15var duck = creator.create("Duck");
16console.log(duck.name); // Duck
17
18var chicken = creator.create("Chicken");
19console.log(chicken.name); // Chicken

实际应用:Button Producer:生产不同类型的按钮 => 生产多个本质相同,利用传参区分不同属性的元素 => 工厂

单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点

1class PlayStation {
2 constructor() {
3 this.state = 'off';
4 }
5 play() {
6 if (this.state === 'on') {
7 console.log('别闹,已经在happy了');
8 return;
9 }
10 this.state = 'on';
11 console.log('开始happy');
12 }
13 shutdown() {
14 if (this.state === 'off') {
15 console.log('已经关闭');
16 return;
17 }
18 this.state = 'off';
19 console.log('已经关机,请放心');
20 }
21}
22PlayStation.instance = undefined;
23PlayStation.getInstance = (function() {
24 return function() {
25 if(!PlayStation.instance) {
26 PlayStation.instance = new PlayStation();
27 }
28 return PlayStation.instance;
29 }()
30}

实际应用:全局应用 router store => 只需要一个实例 => 单例

建造者模式

拆分简单模块、独立执行 => 注重过程与搭配

1class Product {
2 constructor(name) {
3 this.name = name;
4 }
5 init() {
6 console.log("Product init");
7 }
8}
9
10class Skin {
11 constructor(name) {
12 this.name = name;
13 }
14 init() {
15 console.log("Skin init");
16 }
17}
18
19class Shop {
20 constructor() {
21 this.package = "";
22 }
23 create(name) {
24 this.package = new PackageBuilder(name);
25 }
26 getGamePackage() {
27 return this.package.getPackage();
28 }
29}
30
31class PackageBuilder {
32 constructor(name) {
33 this.game = new Product(name);
34 this.skin = new Skin(name);
35 }
36 getPackage() {
37 return this.game.init() + this.skin.init();
38 }
39}

实际应用:页头组件 Header: 包含了 title、button、breadcum => 生产多重不同类型的元素 => 建造者

原型模式

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

在 JavaScript 中,实现原型模式是在 ECMAScript5 中,提出的 Object.create 方法,使用现有的对象来提供新创建的对象的proto

1var prototype = {
2 name: "Jack",
3 getName: function () {
4 return this.name;
5 },
6};
7
8var obj = Object.create(prototype, {
9 job: {
10 value: "IT",
11 },
12});
13
14console.log(obj.getName()); // Jack
15console.log(obj.job); // IT
16console.log(obj.__proto__ === prototype); //true

适配器模式

适配器的作用是解决两个软件实体间接口不兼容的问题,使用适配器模式之后,原本由于接口不兼容而不能工作的两个软件实体可以一起工作。

适配独立模块,保证模块间的独立解耦且连接兼容

1//假如BaiduMap类的原型方法不叫show,而是叫display,这时候就可以使用适配器模式了,因为我们不能轻易的改变第三方的内容。在BaiduMap的基础上封装一层,对外暴露show方法。
2class GooleMap {
3 show() {
4 console.log("渲染谷歌地图");
5 }
6}
7
8class BaiduMap {
9 display() {
10 console.log("渲染百度地图");
11 }
12}
13
14// 定义适配器类, 对BaiduMap类进行封装
15class BaiduMapAdapter {
16 show() {
17 var baiduMap = new BaiduMap();
18 return baiduMap.display();
19 }
20}
21
22function render(map) {
23 if (map.show instanceof Function) {
24 map.show();
25 }
26}
27
28render(new GooleMap()); // 渲染谷歌地图
29render(new BaiduMapAdapter()); // 渲染百度地图

场景:中间转换参数、保持模块间独立的时候

实际应用:两个模块:筛选器和表格,需要做一个联动。但筛选器的数据不能直接传入表格,需要做数据结构转换 ,模块之间独立,需要做数据结构转换

装饰器模式

以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象。 是一种“即用即付”的方式,能够在不改变对 象自身的基础上,在程序运行期间给对象动态地 添加职责

是为对象动态加入行为,经过多重包装,可以形成一条装饰链

1// 动态将责任附加到对象上
2// 设备创建->设备创建时升级
3class Device {
4 create() {
5 console.log("PlayStation4");
6 }
7}
8class Phone {
9 create() {
10 console.log("iphone18");
11 }
12}
13class Decorator {
14 constructor(device) {
15 this.device = device;
16 }
17 create() {
18 this.device.create();
19 this.update(device);
20 }
21 update(device) {
22 console.log(device + "pro");
23 }
24}
25
26const device = new Device();
27device.create();
28
29const newDevice = new Decorator(device);
30newDevice.create();

场景:附着于多个组件上,批量动态赋予功能的时候

实际应用:目前有按钮、title、icon 三个组件。希望开发一个模块,让三个组件同时具备相同功能

代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问

1class MyImage {
2 constructor() {
3 this.img = new Image();
4 document.body.appendChild(this.img);
5 }
6 setSrc(src) {
7 this.img.src = src;
8 }
9}
10
11class ProxyImage {
12 constructor() {
13 this.proxyImage = new Image();
14 }
15
16 setSrc(src) {
17 let myImageObj = new MyImage();
18 myImageObj.img.src = "file://xxx.png"; //为本地图片url
19 this.proxyImage.src = src;
20 this.proxyImage.onload = function () {
21 myImageObj.img.src = src;
22 };
23 }
24}
25
26var proxyImage = new ProxyImage();
27proxyImage.setSrc("http://xxx.png"); //服务器资源url

场景:将代理对象与调用对象分离,不直接调用目标对象

实际应用:ul 中多个 li,每个 li 上的点击事件 => 利用冒泡做委托,事件绑定在 ul 上

策略模式

定义有一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

将算法的使用和算法的实现分离开来。

一个基于策略模式的程序至少由两部分组成:

第一个部分是一组策略类,策略类封装了具体的算法,并负责具体的计算过程。

第二个部分是环境类 Context,Context 接受客户的请求,随后把请求委托给某一个策略类。要做到这点,说明 Context 中要维持对某个策略对象的引用

1// 绩效为S 的人年终奖有4 倍工资,绩效为A 的人年终奖有3 倍工资,而绩效为B 的人年终奖是2 倍工资。
2
3var strategies = {
4 S: function (salary) {
5 return salary * 4;
6 },
7 A: function (salary) {
8 return salary * 3;
9 },
10 B: function (salary) {
11 return salary * 2;
12 },
13};
14var cacularBonus = function (level, salary) {
15 return strategies[level](salary);
16};
17var level2 = cacularBonus("A", 200);
18//我们将计算年终奖的算法,放在一个对象内部,封装起来,在调用的时候可以通过不同的等级获得不同的计算方式。而我们有新的等级或者新的计算方式的时候,我们对该对象进行更改就可以了。避免了在一个函数内部进行计算,提高了可维护性。

迭代器模式

迭代器模式是指提供一种方法顺序访问一个有序聚合对象中的各个元素,而又不需要暴露该对象的内部表示。

在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素

1class Creater {
2 constructor(list) {
3 this.list = list;
4 }
5
6 // 创建一个迭代器,也叫遍历器
7 createIterator() {
8 return new Iterator(this);
9 }
10}
11
12class Iterator {
13 constructor(creater) {
14 this.list = creater.list;
15 this.index = 0;
16 }
17
18 // 判断是否遍历完数据
19 isDone() {
20 if (this.index >= this.list.length) {
21 return true;
22 }
23 return false;
24 }
25
26 next() {
27 return this.list[this.index++];
28 }
29}
30
31var arr = [1, 2, 3, 4];
32
33var creater = new Creater(arr);
34var iterator = creater.createIterator();
35console.log(iterator.list); // [1, 2, 3, 4]
36while (!iterator.isDone()) {
37 console.log(iterator.next());
38 // 1
39 // 2
40 // 3
41 // 4
42}

javascript 中有序数据集合包括

  • Array
  • Map
  • Set
  • String
  • typeArray
  • arguments
  • NodeList
1var arr = [1, 2, 3, 4];
2
3var iterator = arr[Symbol.iterator]();
4
5console.log(iterator.next()); // {value: 1, done: false}
6console.log(iterator.next()); // {value: 2, done: false}
7console.log(iterator.next()); // {value: 3, done: false}
8console.log(iterator.next()); // {value: 4, done: false}
9console.log(iterator.next()); // {value: undefined, done: true}

观察者模式 (订阅发布模式)

也称作观察者模式,定义了对象间的一种一对多的依赖关系,当一个对象的状态发 生改变时,所有依赖于它的对象都将得到通知

取代对象之间硬编码的通知机制,一个对象不用再显式地调用另外一个对象的某个接口。

与传统的发布-订阅模式实现方式(将订阅者自身当成引用传入发布者)不同,在 JS 中通常使用注册回调函数的形式来订阅

1// 当一个属性发生状态改变时,观察者会连续引发所有的相关状态改变
2// 通过智能家居一键开始游戏
3class MediaCenter {
4 constructor() {
5 this.state = "";
6 this.observers = [];
7 }
8 attach(observer) {
9 this.observers.push(observer);
10 }
11 getState() {
12 return this.state;
13 }
14 setState(state) {
15 this.state = state;
16 this.notifyAllobservers();
17 }
18 notifyAllobservers() {
19 this.observers.forEach((ob) => {
20 ob.update();
21 });
22 }
23}
24
25class observer {
26 constructor(name, center) {
27 this.name = name;
28 this.center = center;
29 this.center.attach(this);
30 }
31 update() {
32 console.log(`${this.name} update, state: ${this.center.getState()}`);
33 }
34}
35
36const center = new MediaCenter();
37const ps = new Observer("ps", center);
38const tv = new Observer("tv", center);
39
40center.setState("on");

发布订阅模式可以使代码解耦,满足开放封闭原则 当过多的使用发布订阅模式,如果订阅消息始终都没有触发,则订阅者一直保存在内存中。

命令模式

用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系

命令中带有 execute 执行、undo 撤销、redo 重做等相关命令方法,建议显示地指示这些方法名

1//一个自增命令,提供执行、撤销、重做功能
2
3// 自增
4function IncrementCommand() {
5 // 当前值
6 this.val = 0;
7 // 命令栈
8 this.stack = [];
9 // 栈指针位置
10 this.stackPosition = -1;
11}
12
13IncrementCommand.prototype = {
14 constructor: IncrementCommand,
15
16 // 执行
17 execute: function () {
18 this._clearRedo();
19
20 // 定义执行的处理
21 var command = function () {
22 this.val += 2;
23 }.bind(this);
24
25 // 执行并缓存起来
26 command();
27
28 this.stack.push(command);
29
30 this.stackPosition++;
31
32 this.getValue();
33 },
34
35 canUndo: function () {
36 return this.stackPosition >= 0;
37 },
38
39 canRedo: function () {
40 return this.stackPosition < this.stack.length - 1;
41 },
42
43 // 撤销
44 undo: function () {
45 if (!this.canUndo()) {
46 return;
47 }
48
49 this.stackPosition--;
50
51 // 命令的撤销,与执行的处理相反
52 var command = function () {
53 this.val -= 2;
54 }.bind(this);
55
56 // 撤销后不需要缓存
57 command();
58
59 this.getValue();
60 },
61
62 // 重做
63 redo: function () {
64 if (!this.canRedo()) {
65 return;
66 }
67
68 // 执行栈顶的命令
69 this.stack[++this.stackPosition]();
70
71 this.getValue();
72 },
73
74 // 在执行时,已经撤销的部分不能再重做
75 _clearRedo: function () {
76 this.stack = this.stack.slice(0, this.stackPosition + 1);
77 },
78
79 // 获取当前值
80 getValue: function () {
81 console.log(this.val);
82 },
83};
1var incrementCommand = new IncrementCommand();
2
3// 模拟事件触发,执行命令
4var eventTrigger = {
5 // 某个事件的处理中,直接调用命令的处理方法
6 increment: function () {
7 incrementCommand.execute();
8 },
9
10 incrementUndo: function () {
11 incrementCommand.undo();
12 },
13
14 incrementRedo: function () {
15 incrementCommand.redo();
16 },
17};
18
19eventTrigger["increment"](); // 2
20eventTrigger["increment"](); // 4
21
22eventTrigger["incrementUndo"](); // 2
23
24eventTrigger["increment"](); // 4
25
26eventTrigger["incrementUndo"](); // 2
27eventTrigger["incrementUndo"](); // 0
28eventTrigger["incrementUndo"](); // 无输出
29
30eventTrigger["incrementRedo"](); // 2
31eventTrigger["incrementRedo"](); // 4
32eventTrigger["incrementRedo"](); // 无输出
33
34eventTrigger["increment"](); // 6

状态模式

状态模式允许一个对象在其内部状态改变的时候改变行为,这个对象看上去像是改变了它的类一样,状态模式把所研究对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。

区分事物内部的状态,把事物的每种状态都封装成单独的类,跟此种状态有关的行为都被封装在这个类的内部

1<!DOCTYPE html>
2<html lang="en">
3 <head>
4 <meta charset="UTF-8" />
5 <title>state-demo</title>
6 </head>
7
8 <body>
9 <button id="btn">开关</button>
10 <script>
11 // 定义一个关闭状态的类
12 class OffLightState {
13 constructor(light) {
14 this.light = light;
15 }
16 // 每个类都需要这个方法,在不同状态下按都需要触发这个方法
17 pressBtn() {
18 this.light.setState(this.light.weekLightState);
19 console.log("开启弱光");
20 }
21 }
22
23 // 定义一个弱光状态的类
24 class WeekLightState {
25 constructor(light) {
26 this.light = light;
27 }
28 pressBtn() {
29 this.light.setState(this.light.strongLightState);
30 console.log("开启强光");
31 }
32 }
33
34 // 定义一个强光状态的类
35 class StrongLightState {
36 constructor(light) {
37 this.light = light;
38 }
39 pressBtn() {
40 this.light.setState(this.light.offLightState);
41 console.log("关闭电灯");
42 }
43 }
44
45 class Light {
46 constructor() {
47 this.offLightState = new OffLightState(this);
48 this.weekLightState = new WeekLightState(this);
49 this.strongLightState = new StrongLightState(this);
50 this.currentState = null;
51 }
52 setState(newState) {
53 this.currentState = newState;
54 }
55 checkState(state) {
56 this.currentState = this[state];
57 }
58 }
59
60 let light = new Light();
61 light.checkState("strongLightState");
62 var btn = document.getElementById("btn");
63 btn.onclick = function () {
64 light.currentState.pressBtn();
65 };
66 </script>
67 </body>
68</html>

模板模式

模板方法模式由两部分结构组成,第一部分是抽象父类,第二部分是具体的实现子类。

在抽象父类中封装子类的算法框架,它的 init 方法可作为一个算法的模板,指导子类以何种顺序去执行哪些方法。

由父类分离出公共部分,要求子类重写某些父类的(易变化的)抽象方法

模板方法模式一般的实现方式为继承

以运动作为例子,运动有比较通用的一些处理,这部分可以抽离开来,在父类中实现。具体某项运动的特殊性则有自类来重写实现。

最终子类直接调用父类的模板函数来执行

1// 体育运动
2function Sport() {}
3
4Sport.prototype = {
5 constructor: Sport,
6
7 // 模板,按顺序执行
8 init: function () {
9 this.stretch();
10 this.jog();
11 this.deepBreath();
12 this.start();
13
14 var free = this.end();
15
16 // 运动后还有空的话,就拉伸一下
17 if (free !== false) {
18 this.stretch();
19 }
20 },
21
22 // 拉伸
23 stretch: function () {
24 console.log("拉伸");
25 },
26
27 // 慢跑
28 jog: function () {
29 console.log("慢跑");
30 },
31
32 // 深呼吸
33 deepBreath: function () {
34 console.log("深呼吸");
35 },
36
37 // 开始运动
38 start: function () {
39 throw new Error("子类必须重写此方法");
40 },
41
42 // 结束运动
43 end: function () {
44 console.log("运动结束");
45 },
46};
47
48// 篮球
49function Basketball() {}
50
51Basketball.prototype = new Sport();
52
53// 重写相关的方法
54Basketball.prototype.start = function () {
55 console.log("先投上几个三分");
56};
57
58Basketball.prototype.end = function () {
59 console.log("运动结束了,有事先走一步");
60 return false;
61};
62
63// 马拉松
64function Marathon() {}
65
66Marathon.prototype = new Sport();
67
68var basketball = new Basketball();
69var marathon = new Marathon();
70
71// 子类调用,最终会按照父类定义的顺序执行
72basketball.init();
73marathon.init();

职责链

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这些对象连成一条链,并沿着这条链 传递该请求,直到有一个对象处理它为止

请求发送者只需要知道链中的第一个节点,弱化发送者和一组接收者之间的强联系,可以便捷地在职责链中增加或删除一个节点,同样地,指定谁是第一个节点也很便捷

1class Action {
2 constructor(name) {
3 this.name = name;
4 this.nextAction = null;
5 }
6 setNextAction(action) {
7 this.nextAction = action;
8 }
9 handle() {
10 console.log(`${this.name}请审批,是否可以打游戏`);
11 if (this.nextAction !== null) {
12 this.nextAction.handle();
13 }
14 }
15}
16
17const dad = new Action("爸");
18const mom = new Action("妈");
19const wife = new Action("夫人");
20
21dad.setNextAction(mom);
22mom.setNextAction(wife);
23
24dad.handle();

享元模式

是一种用于性能优化的模式,它的目标是尽量减少共享对象的数量

运用共享技术来有效支持大量细粒度的对象。

调将对象的属性划分为内部状态(属性)与外部状态(属性)。内部状态用于对象的共享,通常不变;而外部状态则剥离开来,由具体的场景决定。

在程序中使用了大量的相似对象时,可以利用享元模式来优化,减少对象的数量

1// 举个栗子,要对某个班进行身体素质测量,仅测量身高体重来评判
2
3// 健康测量
4function Fitness(name, sex, age, height, weight) {
5 this.name = name;
6 this.sex = sex;
7 this.age = age;
8 this.height = height;
9 this.weight = weight;
10}
11
12// 开始评判
13Fitness.prototype.judge = function () {
14 var ret = this.name + ": ";
15
16 if (this.sex === "male") {
17 ret += this.judgeMale();
18 } else {
19 ret += this.judgeFemale();
20 }
21
22 console.log(ret);
23};
24
25// 男性评判规则
26Fitness.prototype.judgeMale = function () {
27 var ratio = this.height / this.weight;
28
29 return this.age > 20 ? ratio > 3.5 : ratio > 2.8;
30};
31
32// 女性评判规则
33Fitness.prototype.judgeFemale = function () {
34 var ratio = this.height / this.weight;
35
36 return this.age > 20 ? ratio > 4 : ratio > 3;
37};
38
39var a = new Fitness("A", "male", 18, 160, 80);
40var b = new Fitness("B", "male", 21, 180, 70);
41var c = new Fitness("C", "female", 28, 160, 80);
42var d = new Fitness("D", "male", 18, 170, 60);
43var e = new Fitness("E", "female", 18, 160, 40);
44
45// 开始评判
46a.judge(); // A: false
47b.judge(); // B: false
48c.judge(); // C: false
49d.judge(); // D: true
50e.judge(); // E: true

More articles from Favori 重剑

babel核心

babel核心介绍

October 7th, 2021 · 1 min read

React 15 和 React 16 的区别

react-15-16

October 6th, 2021 · 1 min read
© 2016–2023 Favori 重剑
Link to $https://github.com/yuanguandongLink to $https://favori.zcool.com.cn/Link to $https://codepen.io/favori