fix: 设计模式添加
This commit is contained in:
parent
642f06dfdf
commit
79143a69eb
69
docs/fea/js/index.md
Normal file
69
docs/fea/js/index.md
Normal file
@ -0,0 +1,69 @@
|
||||
---
|
||||
nav:
|
||||
title: 前端
|
||||
path: /fea
|
||||
group:
|
||||
title: 💊 JavaScript
|
||||
order: 2
|
||||
path: /js
|
||||
---
|
||||
|
||||
# 💊 JavaScript
|
||||
|
||||
## 深拷贝 VS 浅拷贝
|
||||
|
||||
> 区别:浅拷贝只拷贝一层,深拷贝无限层级拷贝
|
||||
|
||||
### 浅拷贝(方案 1)
|
||||
|
||||
遍历
|
||||
|
||||
```js
|
||||
function shallowClone(source) {
|
||||
var target = {};
|
||||
for (var i in source) {
|
||||
if (source.hasOwnProperty(i)) {
|
||||
target[i] = source[i];
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
```
|
||||
|
||||
### 深拷贝(方案 1)
|
||||
|
||||
遍历 + 递归
|
||||
|
||||
**该方法出现的问题是:爆栈。当层级太深的时候会发生**
|
||||
|
||||
```js
|
||||
function isObject(x) {
|
||||
return Object.prototype.toString.call(x) === '[object Object]';
|
||||
}
|
||||
|
||||
function deepClone(source) {
|
||||
if (!isObject(source)) return source;
|
||||
|
||||
var target = {};
|
||||
for (var i in source) {
|
||||
if (source.hasOwnProperty(i)) {
|
||||
if (typeof source[i] === 'object') {
|
||||
target[i] = deepClone(source[i]); // 注意这里
|
||||
} else {
|
||||
target[i] = source[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
```
|
||||
|
||||
### 深拷贝(方案 2)
|
||||
|
||||
```js
|
||||
function cloneJSON(source) {
|
||||
return JSON.parse(JSON.stringify(source));
|
||||
}
|
||||
```
|
@ -9,56 +9,65 @@ group:
|
||||
|
||||
## 关于
|
||||
|
||||
学习设计模式,是为了让你的代码减少亢余,能用健壮的代码去解决问题,提升可维护性与拓展性。
|
||||
|
||||
**想做靠谱开发,先学设计模式**
|
||||
|
||||
### 最终目标
|
||||
|
||||
1. 充分理解前端设计模式的核心思想和基本理念,在具体的场景中掌握抽象的设计原则
|
||||
2. 会写代码,会写好代码
|
||||
3. 会面试,能言之有物
|
||||
|
||||
## SOLID 设计原则
|
||||
|
||||
设计模式需要遵循五个原则,被称为 SOLID 原则,内容分别是:
|
||||
|
||||
设计模式需要遵循五个原则,被称为SOLID原则,内容分别是:
|
||||
- 单一功能原则(Single Responsibility Principle)【_重点_】
|
||||
- 开放封闭原则(Opened Closed Principle)【_重点_】
|
||||
- 里式替换原则(Liskov Substitution Principle)
|
||||
- 接口隔离原则(Interface Segregation Principle)
|
||||
- 依赖反转原则(Dependency Inversion Principle)
|
||||
|
||||
* 单一功能原则(Single Responsibility Principle)
|
||||
* 开放封闭原则(Opened Closed Principle)
|
||||
* 里式替换原则(Liskov Substitution Principle)
|
||||
* 接口隔离原则(Interface Segregation Principle)
|
||||
* 依赖反转原则(Dependency Inversion Principle)
|
||||
> 将变与不变分离,确保变化的部分灵活,不变的部分稳定。
|
||||
|
||||
> "SOLID" 是由罗伯特·C·马丁在 21 世纪早期引入的记忆术首字母缩略字,指代了面向对象编程和面向对象设计的五个基本原则。
|
||||
|
||||
## 设计模式分类(早期22种,新增1种)
|
||||
## 设计模式分类(早期 22 种,新增 1 种)
|
||||
|
||||
### 创建型
|
||||
|
||||
创建型模式封装了创建对象过程中的变化,它做的事情就是将创建对象的过程抽离;
|
||||
|
||||
* 单例模式
|
||||
* 原型模式
|
||||
* 构造器模式
|
||||
* 工厂模式
|
||||
* 抽象工厂模式
|
||||
* 建造者(新)
|
||||
- 单例模式
|
||||
- 原型模式
|
||||
- 构造器模式
|
||||
- 工厂模式
|
||||
- 抽象工厂模式
|
||||
- 建造者(新)
|
||||
|
||||
### 结构型
|
||||
|
||||
结构型模式封装的是对象之间组合方式的变化,目的在于灵活地表达对象间的配合与依赖关系;
|
||||
|
||||
* 桥接模式
|
||||
* 外观模式
|
||||
* 组合模式
|
||||
* 装饰器模式
|
||||
* 适配器模式
|
||||
* 代理模式
|
||||
* 享元模式
|
||||
- 桥接模式
|
||||
- 外观模式
|
||||
- 组合模式
|
||||
- 装饰器模式
|
||||
- 适配器模式
|
||||
- 代理模式
|
||||
- 享元模式
|
||||
|
||||
### 行为型
|
||||
|
||||
而行为型模式则将是对象千变万化的行为进行抽离,确保我们能够更安全、更方便地对行为进行更改
|
||||
|
||||
* 迭代器模式
|
||||
* 解释器模式
|
||||
* 观察者模式
|
||||
* 中介者模式
|
||||
* 访问者模式
|
||||
* 状态模式
|
||||
* 备忘录模式
|
||||
* 策略模式
|
||||
* 模板方法模式
|
||||
* 职责链模式
|
||||
* 命令模式
|
||||
- 迭代器模式
|
||||
- 解释器模式
|
||||
- 观察者模式
|
||||
- 中介者模式
|
||||
- 访问者模式
|
||||
- 状态模式
|
||||
- 备忘录模式
|
||||
- 策略模式
|
||||
- 模板方法模式
|
||||
- 职责链模式
|
||||
- 命令模式
|
||||
|
@ -17,40 +17,40 @@ group:
|
||||
|
||||
#### 简单版
|
||||
|
||||
场景是我需要创建500个用户,他们分别有不同的角色,页面访问不同权限。
|
||||
场景是我需要创建 500 个用户,他们分别有不同的角色,页面访问不同权限。
|
||||
|
||||
```js
|
||||
class User {
|
||||
constructor(name, type, viewPage) {
|
||||
this.type = type
|
||||
this.name = name
|
||||
this.viewPage = viewPage
|
||||
}
|
||||
class User {
|
||||
constructor(name, type, viewPage) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.viewPage = viewPage;
|
||||
}
|
||||
}
|
||||
|
||||
export default function getUserTypeFactory(name, type) {
|
||||
let viewPage
|
||||
switch (type) {
|
||||
case 'superAdmin':
|
||||
viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理']
|
||||
break;
|
||||
case 'admin':
|
||||
viewPage = ['首页', '通讯录', '发现页', '应用数据']
|
||||
break;
|
||||
case 'normal':
|
||||
viewPage = ['首页', '通讯录', '发现页']
|
||||
break;
|
||||
default:
|
||||
throw new Error('参数错误,可选参数:superAdmin、admin、normal')
|
||||
}
|
||||
let viewPage;
|
||||
switch (type) {
|
||||
case 'superAdmin':
|
||||
viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理'];
|
||||
break;
|
||||
case 'admin':
|
||||
viewPage = ['首页', '通讯录', '发现页', '应用数据'];
|
||||
break;
|
||||
case 'normal':
|
||||
viewPage = ['首页', '通讯录', '发现页'];
|
||||
break;
|
||||
default:
|
||||
throw new Error('参数错误,可选参数:superAdmin、admin、normal');
|
||||
}
|
||||
|
||||
return new User(name, type, viewPage)
|
||||
return new User(name, type, viewPage);
|
||||
}
|
||||
```
|
||||
|
||||
### 抽象工厂模式
|
||||
|
||||
结合上文中的工厂,发现它不完全遵循 **SOLID法则** 中的开放封闭原则:对拓展开放,对修改封闭。
|
||||
结合上文中的工厂,发现它不完全遵循 **SOLID 法则** 中的开放封闭原则:对拓展开放,对修改封闭。
|
||||
|
||||
这篇文章我用一个手机从出产到上市使用的一个创建过程,首先我们知道,手机由两部分组成 **软件** ,**硬件**:
|
||||
|
||||
@ -59,12 +59,12 @@ export default function getUserTypeFactory(name, type) {
|
||||
class AbstractFactory {
|
||||
// 操作系统接口
|
||||
createOS() {
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写')
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写');
|
||||
}
|
||||
|
||||
// 提供硬件接口
|
||||
createHardWare() {
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写')
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写');
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,34 +73,34 @@ class FakeStarFactory extends AbstractFactory {
|
||||
// 操作系统接口
|
||||
createOS() {
|
||||
// 安卓系统实例
|
||||
return new AndroidOS()
|
||||
return new AndroidOS();
|
||||
}
|
||||
|
||||
// 提供硬件接口
|
||||
createHardWare() {
|
||||
// 高通硬件实例
|
||||
return new QualcommHardWare()
|
||||
return new QualcommHardWare();
|
||||
}
|
||||
}
|
||||
|
||||
// 2-1. 抽象产品(软件):只定义
|
||||
class OS {
|
||||
controlHardWare() {
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写')
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写');
|
||||
}
|
||||
}
|
||||
|
||||
// 2-2. 具体产品:安卓操作系统
|
||||
class AndroidOS extends OS {
|
||||
controlHardWare() {
|
||||
console.log('我会用安卓的方法去运行操作系统')
|
||||
console.log('我会用安卓的方法去运行操作系统');
|
||||
}
|
||||
}
|
||||
|
||||
// 2-2. 具体产品:苹果操作系统
|
||||
class AppleOS extends OS {
|
||||
controlHardWare() {
|
||||
console.log('我会用苹果的方法去运行操作系统')
|
||||
console.log('我会用苹果的方法去运行操作系统');
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,21 +108,21 @@ class AppleOS extends OS {
|
||||
class HardWare {
|
||||
// 手机硬件的共性方法:根据命令运转
|
||||
operateByOrder() {
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写')
|
||||
throw new Error('抽象方法不允许直接调用,你需要将我重写');
|
||||
}
|
||||
}
|
||||
|
||||
// 3-2. 具体产品(硬件):高通
|
||||
class QualcommHardWare extends HardWare {
|
||||
operateByOrder() {
|
||||
console.log('我会用高通的方式去运转')
|
||||
console.log('我会用高通的方式去运转');
|
||||
}
|
||||
}
|
||||
|
||||
// 3-2. 具体产品(硬件):英特尔
|
||||
class InterHardWare extends HardWare {
|
||||
operateByOrder() {
|
||||
console.log('我会用英特尔的方式去运转')
|
||||
console.log('我会用英特尔的方式去运转');
|
||||
}
|
||||
}
|
||||
|
||||
@ -134,22 +134,20 @@ const myOS = myMobile.createOS();
|
||||
const myHardWare = myOS.createHardWare();
|
||||
|
||||
// 启动操作系统
|
||||
myOS.controlHardWare()
|
||||
myOS.controlHardWare();
|
||||
|
||||
// 唤醒硬件命令
|
||||
myHardWare.operateByOrder()
|
||||
myHardWare.operateByOrder();
|
||||
```
|
||||
|
||||
以上就是我们通过场景创建的四个重要的角色:
|
||||
|
||||
* 抽象工厂(抽象类,它不能被用于生成具体实例): 用于声明最终目标产品的共性。在一个系统里,抽象工厂可以有多个(大家可以想象我们的手机厂后来被一个更大的厂收购了,这个厂里除了手机抽象类,还有平板、游戏机抽象类等等),每一个抽象工厂对应的这一类的产品,被称为“产品族”。
|
||||
* 具体工厂(用于生成产品族里的一个具体的产品): 继承自抽象工厂、实现了抽象工厂里声明的那些方法,用于创建具体的产品的类。
|
||||
* 抽象产品(抽象类,它不能被用于生成具体实例): 上面我们看到,具体工厂里实现的接口,会依赖一些类,这些类对应到各种各样的具体的细粒度产品(比如操作系统、硬件等),这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
|
||||
* 具体产品(用于生成产品族里的一个具体的产品所依赖的更细粒度的产品): 比如我们上文中具体的一种操作系统、或具体的一种硬件等。
|
||||
|
||||
总结下来,抽象工厂就类似 typescript 里面的 interface 申明对象。
|
||||
|
||||
- 抽象工厂(抽象类,它不能被用于生成具体实例): 用于声明最终目标产品的共性。在一个系统里,抽象工厂可以有多个(大家可以想象我们的手机厂后来被一个更大的厂收购了,这个厂里除了手机抽象类,还有平板、游戏机抽象类等等),每一个抽象工厂对应的这一类的产品,被称为“产品族”。
|
||||
- 具体工厂(用于生成产品族里的一个具体的产品): 继承自抽象工厂、实现了抽象工厂里声明的那些方法,用于创建具体的产品的类。
|
||||
- 抽象产品(抽象类,它不能被用于生成具体实例): 上面我们看到,具体工厂里实现的接口,会依赖一些类,这些类对应到各种各样的具体的细粒度产品(比如操作系统、硬件等),这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
|
||||
- 具体产品(用于生成产品族里的一个具体的产品所依赖的更细粒度的产品): 比如我们上文中具体的一种操作系统、或具体的一种硬件等。
|
||||
|
||||
总结下来,抽象工厂就类似 typescript 里面的 interface 申明对象,使用工厂模式的目的,就是为了实现**无脑传参**,就是为了爽!
|
||||
|
||||
### 单例模式
|
||||
|
||||
@ -160,22 +158,22 @@ myHardWare.operateByOrder()
|
||||
```js
|
||||
// ------------------- 单例模式 -------------------
|
||||
export default class Singleton {
|
||||
show() {
|
||||
console.log('我是一个单例对象')
|
||||
}
|
||||
show() {
|
||||
console.log('我是一个单例对象');
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new Singleton()
|
||||
}
|
||||
return this.instance
|
||||
static getInstance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new Singleton();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
const s1 = Singleton.getInstance()
|
||||
const s2 = Singleton.getInstance()
|
||||
const s1 = Singleton.getInstance();
|
||||
const s2 = Singleton.getInstance();
|
||||
|
||||
s1 === s2 // => true
|
||||
s1 === s2; // => true
|
||||
```
|
||||
|
||||
#### 进阶版
|
||||
@ -185,25 +183,24 @@ s1 === s2 // => true
|
||||
```js
|
||||
class Storage {
|
||||
constructor() {
|
||||
this.instance = null
|
||||
this.instance = null;
|
||||
}
|
||||
|
||||
static getInstance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new Storage()
|
||||
this.instance = new Storage();
|
||||
}
|
||||
return this.instance
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
getItem(key) {
|
||||
return localStorage.getItem(key)
|
||||
return localStorage.getItem(key);
|
||||
}
|
||||
|
||||
setItem(key, value) {
|
||||
return localStorage.setItem(key, value)
|
||||
return localStorage.setItem(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### 构造器模式
|
||||
@ -214,16 +211,16 @@ class Storage {
|
||||
// 一个构造函数
|
||||
class Asshole {
|
||||
constructor(name, age) {
|
||||
this.name = name
|
||||
this.age = age
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
say() {
|
||||
console.log(`I'm asshole ${this.name},${this.age} years old.`)
|
||||
console.log(`I'm asshole ${this.name},${this.age} years old.`);
|
||||
}
|
||||
}
|
||||
|
||||
const me = new Asshole('json', 28)
|
||||
const me = new Asshole('json', 28);
|
||||
```
|
||||
|
||||
**谈原型模式,其实是谈原型范式**,原型编程范式的核心思想就是利用实例来描述对象,用实例作为定义对象和继承的基础。在 JavaScript 中,原型编程范式的体现就是基于原型链的继承。这其中,对 **原型、原型链** 的理解是关键。
|
||||
@ -235,26 +232,25 @@ const me = new Asshole('json', 28)
|
||||
```ts
|
||||
const deepClone = (obj) => {
|
||||
// 如果是值类型或者不存在,直接返回
|
||||
if (typeof obj !== 'object' || obj === null) return obj
|
||||
if (typeof obj !== 'object' || obj === null) return obj;
|
||||
|
||||
// 定义结果对象
|
||||
let copy = {}
|
||||
let copy = {};
|
||||
|
||||
// 如果对象是数组,则定义结果数组
|
||||
if (obj.constructor === Array) copy = []
|
||||
if (obj.constructor === Array) copy = [];
|
||||
|
||||
// 遍历对象的 key
|
||||
for (let key in obj) {
|
||||
// 如果key是对象的自身属性
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
// 递归调用深拷贝方法
|
||||
copy[key] = deepClone(obj[key])
|
||||
}
|
||||
}
|
||||
|
||||
return copy
|
||||
}
|
||||
// 遍历对象的 key
|
||||
for (let key in obj) {
|
||||
// 如果key是对象的自身属性
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
// 递归调用深拷贝方法
|
||||
copy[key] = deepClone(obj[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
};
|
||||
```
|
||||
|
||||
### 建造者模式
|
||||
@ -262,40 +258,40 @@ const deepClone = (obj) => {
|
||||
```js
|
||||
// 建造者模式 需要优化
|
||||
export default class Builder {
|
||||
constructor() {
|
||||
this.name = ''
|
||||
this.author = ''
|
||||
this.price = 0
|
||||
this.category = ''
|
||||
}
|
||||
constructor() {
|
||||
this.name = '';
|
||||
this.author = '';
|
||||
this.price = 0;
|
||||
this.category = '';
|
||||
}
|
||||
|
||||
withName(name) {
|
||||
this.name = name
|
||||
return this
|
||||
}
|
||||
withName(name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
withAuthor(author) {
|
||||
this.author = author
|
||||
return this
|
||||
}
|
||||
withAuthor(author) {
|
||||
this.author = author;
|
||||
return this;
|
||||
}
|
||||
|
||||
withPrice(price) {
|
||||
this.price = price
|
||||
return this
|
||||
}
|
||||
withPrice(price) {
|
||||
this.price = price;
|
||||
return this;
|
||||
}
|
||||
|
||||
withCategory(category) {
|
||||
this.category = category
|
||||
return this
|
||||
}
|
||||
withCategory(category) {
|
||||
this.category = category;
|
||||
return this;
|
||||
}
|
||||
|
||||
build() {
|
||||
return {
|
||||
name: this.name,
|
||||
author: this.author,
|
||||
price: this.price,
|
||||
category: this.category
|
||||
}
|
||||
}
|
||||
build() {
|
||||
return {
|
||||
name: this.name,
|
||||
author: this.author,
|
||||
price: this.price,
|
||||
category: this.category,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user