千锋教育-做有情怀、有良心、有品质的职业教育机构

400-811-9990
手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

上海
  • 北京
  • 郑州
  • 武汉
  • 成都
  • 西安
  • 沈阳
  • 广州
  • 南京
  • 深圳
  • 大连
  • 青岛
  • 杭州
  • 重庆
当前位置:大连千锋IT培训  >  技术干货  >  JavaScript全解析——设计模式是什么

JavaScript全解析——设计模式是什么

来源:千锋教育
发布人:bjq
时间: 2023-05-11 13:51:00

  JavaScript全解析——设计模式是什么

      ●为了解决一类问题给出的 简洁而优化 的解决方案

  ●设计模式不是规则,是程序员开发过程中的经验总结

  单例模式

  ●一个构造函数一生只有一个 实例对象

<script>
function Person(name) {
this.name = name
}
// 核心代码
let instance = null
function singleton() {
if (!instance) instance = new Person('Jack')
return instance
}
// 实现单例模式创建实例对象
// 第一次调用 singleton() 函数, 此时 instance 就是 null
// if 条件为 true, 给 instance 赋值
// 赋值为一个 Person 的实例
// return instance, 把 instance 保存的值赋值给了 p1
// p1 拿到的是 Person 的实例
const p1 = singleton()
// 第二次调用 singleton() 函数, 此时 instance 是Perosn 的实例
// if 条件为 false, 后面的代码不执行
// 直接执行 return instance
// 又一次把 Person 的实例返回了
// 赋值给了 p2 变量
// p2 拿到的还是 Person 之前的哪一个实例
const p2 = singleton()
console.log(p1, p2)
console.log(p1 === p2)
</script>

 

  单例模式变形

  ●利用闭包和自执行函数结合的方式实现

function Person(name) {
this.name = name
}
let instance = null
function singleton() {
if (!instance) instance = new Person('Jack')
return instance
}
// 我们要以闭包的形式得到singleton 不让都暴露在全局
// 这里我们要使用的就是自执行函数
const singleton = (function outer() {
function Person(name) {
this.name = name
}
let instance = null
return function inner() {
if (!instance) instance = new Person('Jack')
return instance
}
})()
// const p1 = singleton()
// const p2 = singleton()
// console.log(p1, p2);
// console.log(p1 === p2);
// 到了这里我们该放进去的都放进去了接下来我们继续变形
/*
Person这个变量名现在只在函数里面有作用
如果我现在把singleton变成Person 和里面的Person有没有影响?
是没有影响的
我们先不改变 为了好说
现在要弄明白一个问题 就是外面的singleton()调用的是那个函数
*/

const singleton = (function outer() {
function Person(name) {
this.name = name
}
// 在Person的原型对象上定义一个方法 用来修改Person构造函数里面的name的值
Person.prototype.setName = function (val) { this.name = val }
// 单例的核心代码
let instance = null
return function inner(name) {
if (!instance) instance = new Person()
// 这里调用setName函数
instance.setName(name)
// 这个位置的instance就是person的实例
return instance
}
})()
// const p1 = singleton('Rose')
console.log(p1);
// const p2 = singleton('Jack')
console.log(p2);
console.log(p1 === p2);
// 再次修改变量
const Person = (function outer() {
function Person(name) {
this.name = name
}
// 在Person的原型对象上定义一个方法 用来修改Person构造函数里面的name的值
Person.prototype.setName = function (val) { this.name = val }
// 单例的核心代码
let instance = null
return function inner(name) {
if (!instance) instance = new Person()
// 这里调用setName函数
instance.setName(name)
// 这个位置的instance就是person的实例
return instance
}
})()
// 所以这个时候我们写new也没有关系
// 因为return的是一个复杂数据类型 自动创建的能力没有了
const p1 = new Person('Rose')
console.log(p1);
const p2 = new Person('Jack')
console.log(p2);
console.log(p1 === p2);

 

  案例-自定义弹出层

  结构

   <!-- 整个弹出层 -->
<div class="dialog">
<!-- 顶部内容 -->
<div class="top">
<p>提示</p>
<span>X</span>
</div>
<!-- 中间内容区域 -->
<div class="content">
真实内容
</div>
<!-- 底部区域 -->
<div class="bottom">
<button>确定</button>
</div>
</div>

 

  样式


.dialog {
width: 600px;
height: 360px;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
box-shadow: 1px 2px 2px 0px #ccc;
background-color: #fff;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
display: flex;
flex-direction: column;
border-radius: 15px;
}
.dialog > .top {
height: 45px;
background-color: skyblue;
display: flex;
box-sizing: border-box;
justify-content: space-between;
padding: 0 20px;
align-items: center;
border-bottom: 1px solid #ccc;
border-radius: 15px 15px 0 0;
}
.dialog > .top > p {
font-size: 22px;
font-weight: 700;
color: #fff;
}
.dialog > .top > span {
cursor: pointer;
}
.dialog > .bottom {
height: 45px;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #ccc;
}
.dialog > .bottom > button {
font-size: 20px;
padding: 0 10px;
cursor: pointer;
}
.dialog > .content {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
}

 

  交互

// 书写单例模式代码
const Dialog = (function () {

// 构造函数体
class Dialog {
constructor () {
this.dialog = document.createElement('div')
// 顶部面板
this.top = null
// 面板内的文本
this.desc = null
// 中间的内容区域
this.content = null
// 底部
this.bottom = null


// 面本内的文本
this.descText = '提示'
// 提示文本内容
this.title = '你好 世界'
// 颜色和文本你对照表
this.list = [
{
name: 'success',
descText: '成功',
bgColor: 'green'
},
{
name: 'danger',
descText: '危险',
bgColor: 'red'
},
{
name: 'warning',
descText: '警告',
bgColor: 'orange'
},
{
name: 'default',
descText: '提示',
bgColor: '#fff'
},
{
name: 'info',
descText: '信息',
bgColor: 'skyblue'
}
]

this.single()
}

// 只执行一次
single () {
this.creHTML()
this.setCss()
this.bindEvent()
}

// 创建整体结构
creHTML () {
// 创建 top 结构
this.top = document.createElement('div')
this.desc = document.createElement('p')
this.desc.innerText = this.descText
this.span = document.createElement('span')
this.span.innerText = 'X'
// 把 p 和 span 插入到 top 内
this.top.appendChild(this.desc)
this.top.appendChild(this.span)


// 创建内容结构
this.content = document.createElement('div')
this.content.innerText = this.title

// 创建底部结构
this.bottom = document.createElement('div')
this.btn = document.createElement('button')
this.btn.innerText = '确定'
this.bottom.appendChild(this.btn)

// 把创建好的结构插入到 dialog 内
this.dialog.appendChild(this.top)
this.dialog.appendChild(this.content)
this.dialog.appendChild(this.bottom)

// 把创建好的 dialog 结构插入到 页面内
document.body.appendChild(this.dialog)
}

// 设置 css 样式
setCss () {
// 给 dialog 设置样式
setStyles(this.dialog, {
width: '600px',
height: '360px',
'border-top': '1px solid #ccc',
'border-left': '1px solid #ccc',
'box-shadow': '1px 2px 2px 0px #ccc',
'background-color': '#fff',
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
margin: 'auto',
display: 'flex',
'flex-direction': 'column',
'border-radius': '15px',
})

// 给 top 设置样式
setStyles(this.top, {
height: '45px',
'background-color': 'skyblue',
display: 'flex',
'box-sizing': 'border-box',
'justify-content': 'space-between',
padding: '0 20px',
'align-items': 'center',
'border-bottom': '1px solid #ccc',
'border-radius': '15px 15px 0 0'
})

// 给 content 设置样式
setStyles(this.content, {
flex: 1,
display: 'flex',
'justify-content': 'center',
'align-items': 'center',
'font-size': '20px'
})

// 给 bottom 设置样式
setStyles(this.bottom, {
height: '45px',
display: 'flex',
'align-items': 'center',
'justify-content': 'center',
'border-top': '1px solid #ccc'
})

// 给 面板提示 设置样式
setStyles(this.desc, {
'font-size': '22px',
'font-weight': 700
})

// 给 面板的 关闭按钮
setStyles(this.span, {
cursor: 'pointer'
})

// 底部按钮
setStyles(this.btn, {
'font-size': '20px',
padding: '0 10px',
cursor: 'pointer'
})
}

// 事件绑定
bindEvent () {
this.btn.addEventListener('click', () => {
this.dialog.style.display = 'none'
})
this.span.addEventListener('click', () => {
this.dialog.style.display = 'none'
})
}

// 每次都要执行
init (title = '', type = 'default') {
// 根据你的 文本 和 关键字 设置内容
// 1. 根据类型获取到对照表内的对应信息
const info = this.list.find(item => item.name === type) || { name: 'default', descText: '提示', bgColor: '#fff' }
// 2. 根据 info 的内容开始设置
this.top.style.backgroundColor = info.bgColor
this.desc.innerText = info.descText
this.descText = info.descText
this.content.innerText = title
this.title = title

// 让 dialog 显示出来
this.dialog.style.display = 'flex'
}
}

// 功能函数
function setStyles(ele, styles) {
for (let k in styles) {
ele.style[k] = styles[k]
}
}

// 单例模式核心代码
let instance = null
return function (...arg) {
if (!instance) instance = new Dialog()
instance.init(...arg)
return instance
}
})()

 

  发布订阅模式


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
/*
发布订阅模式
例子: 去书店买书
没有设计模式 就是面向过程
=> 去到书店 , 问有没有书 , 没有就回去了,
=> 过一会儿 , 在去书店问问有没有书 , 没有就回去
=> 过一会儿 , 在去书店问问有没有书 , 没有就回去
=> 过一会儿 , 在去书店问问有没有书 , 没有就回去
=> 过一会儿 , 在去书店问问有没有书 , 没有就回去
=> 过一会儿 , 在去书店问问有没有书 , 没有就回去
=> 过一会儿 , 在去书店问问有没有书 , 没有就回去
=> .....
=> 过一会儿 , 去到书店问问有没有书 , 有了 , 买回去
发布订阅模式
=> 去到书店 , 问有没有书 , 没有的话留下一个电话号码 , 有了给我打电话
=> 回去等待电话
=> 接到电话了去到书店把书买回去

发布订阅: 把你想要做的事情都写好(也就是注册到on函数中) , 将来一触发(就是执行了trigger函数)就都做完了
*/


// 代码实现
class Observer{
constructor(status){
// 表示你的初始状态 , 将来一旦这个状态发生了改变我们要触发
// 也就是一旦有书了要通知人来买
this.status = status

// 定义个消息盒子 , 记录的都有谁和我定了书
// 定了几本书 , 都有那些书 , 是不是要记录上啊
this.message = {}
}

// 在原型上定义几个方法
// 这个表示注册一个事件
// 下面函数调用了传递了实参 , 我们这里要接受形参
on (type,fn) {
// type 事件类型
// fn 事件处理函数
// 接下来我们要绑定在message内部
// 我们要判断message内部有没有type这个类型
// 如果没有我们就让它称为一个数组
if(!this.message[type]) this.message[type] = []
// 之后我们向向消息盒子内添加函数
this.message[type].push(fn)
}

// 这个表示取消这个事件
// 也要接收两个参数
off (type , fn) {
// 这个时候我们也要先判断消息盒子中有没有这个类型
// 如果没有 , 我们就什么都不用做了
if(!this.message[type]) return
// 代码能执行到这里说名有这个函数
// 那我们就解绑这个函数就好了
// 如何解绑呢?
this.message[type] = this.message[type].filter(item => item !== fn) // 把不一样的重新赋值 , 那就把一样的筛出去了
}

// 触发事件
trigger (type) {
// 首先要判断有没有这个事件 , 如果没有就什么都不做了
if(!this.message[type]) return
// 代码能执行到这里说明有这个事件
// 那接下来就触发这个事件就好了
// 里面就是每一个函数 , 直接调用就可以了
this.message[type].forEach(item => item())
}
}

// 实例化个对象
// 这就是一个第三方 , 你可以把这个当做是书店的店员
const o = new Observer('没有')
// console.log(o);

// 准备几个函数
function handlerA() { console.log('handlerA');}
function handlerB() { console.log('handlerB');}
function handlerC() { console.log('handlerC');}

// 向o 上注册一个事件
// js这本书来了 , 执行handlerA这个函数
o.on('js',handlerA)
o.on('js',handlerB)
o.on('css',handlerA)
o.on('css',handlerC)
o.on('html',handlerB)
o.on('html',handlerC)
console.log(o);

// 向 o 取消一个事件注册
// 也就是我从别的地方买到这本书了 , 不要在给我打电话了
o.off('js',handlerA)

// 都注册好了以后由店员来触发这个事件
o.trigger('js') // 表示js这本书来了 , 这样就触发了这个函数

</script>
</body>
</html>

 

  策略模式

  ●也是设计模式的一种 , 也是相对来说用的比较多的一种

  ●作用:

  ○减少if条件的判断

  ○就是把多种形式的内容罗列出来

  ●核心

  ○就是用一个数据结构 , 把各种操作记录下来

  ○值存储的就是操作后的结果

  ●例子

  ○需求:

  => 我已知一个购物总价 1356

  => 需要根据折扣计算最终价格

  => 折扣种类 8折 7折 300-20 500-50

  实现:

  => 我们需要在标签上记录一个属性

  => 能记录到我们的各种折扣

  => 如果折扣有很多呢? 如果后期需要添加或者减少折扣呢?

  => 这样操作就能麻烦

  ●之前的实现方式


const type = '80%' // '70%' '300-20' '500-50'
if (type === '80%') {
} else if (type === '70%') {
console.log('逻辑实现');
} else if (type === '300-20') {
console.log('逻辑实现');
} else if (type === '500-50') {
console.log('逻辑实现');
} else if (type === '300-30') {
console.log('逻辑实现');
}

 

  策略模式


// 我们利用的还是闭包
const discount = (function () {
// 留存一份数据结构
const discountList = {
// 这里的数据要如何设计
// 需要有折扣类型和折扣以后的价格
// 标识:能计算出最后的价格(这里的将来需要调用实现 , 也就是这里需要一个函数)
'80%':function (total) { return (total * 0.8).toFixed(2) }
}
// 因为要使用闭包 , 我们需要返回一个函数
function inner(total,type) {
// 这里我们出计算方式
// 这里需要根据传递进来的折扣查看discountList中有没有这个折扣就好了
// 需要把 type 当做键来访问 discountList
// 如果有返回的是一个函数 , 如果没有返回的是一个undefined
// console.log(discountList[type]);
// 判断有没有这个折扣 , 没有这个折扣就返回总价
if (!discountList[type]) return total
// 代码能执行到这里说明是有这个折扣的
return discountList[type](total) - 0
}
// 这里把inner函数当做一个对象来看待 , 向里面插入一些方法
// 添加一个方法 , 专门用来向 discountList内添加折扣类型
inner.add = function (type,fn) {
// type:表示添加的折扣
// fn: 是一个函数,用来计算出最后的价格
// console.log(type);
// console.log(fn);
// 添加到 discountList中
discountList[type] = fn
}
// 添加一个方法 , 用来删除一种折扣
inner.remove = function (type) {
// 删除掉这个折扣
delete discountList[type]
}
// 我们含可以把这个折扣的列表返回出去查看
inner.getList = function () {
return discountList
}
// 这里返回inner函数
return inner
})()
// 将来使用
// const res = discount('总价','折扣类型')
const res = discount(1000,'80%')
console.log(res);
// 将来使用添加的折扣
discount.add('300-20', price => price - parseInt(price / 300) * 20)
// 添加完毕以后需要在下面计算
const res1 = discount(1000,'300-20')
console.log(res1);
// 查看折扣列表
const list = discount.getList()
console.log(list);

 

  案例-收银台


<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}

body {
padding-left: 200px;
}

input, button {
outline: none;
}

ul, li {
list-style: none;
}

.addSale, .calc {
width: 200px;
height: 33px;
background-color: skyblue;
color: #fff;
cursor: pointer;
border: none;
margin: 30px;
}

input {
width: 300px;
height: 30px;
padding-left: 20px;
font-size: 22px;
display: block;
margin: 20px;
}

ul {
display: flex;
margin: 20px;
}

ul > li {
width: 120px;
height: 120px;
background-color: orange;
margin: 10px;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 30px;
cursor: pointer;
}

ul > li.active {
background-color: skyblue;
}

p {
font-size: 100px;
color: red;
margin: 20px;
}

</style>
</head>
<body>

<button class="addSale">添加折扣类型</button>

<!-- 总价格 -->
<input type="text">

<!-- 折扣类型 -->
<ul>
<li class="active">1111</li>
<li>1111</li>
<li>1111</li>
</ul>

<button class="calc">计算总价</button>

<!-- 最终价格 -->
<p>0.00</p>


折扣名称(显示在按钮上的文本): <input type="text" class="type">
计算方式(总价以 x 表示): <input type="text" class="method">

<script>
/*
收银台
*/


// 0. 获取元素
const ulBox = document.querySelector('ul')
const calcBtn = document.querySelector('.calc')
const totalInp = document.querySelector('input')
const pBox = document.querySelector('p')

const addBtn = document.querySelector('.addSale')
const nameInp = document.querySelector('.type')
const methodInp = document.querySelector('.method')

// 0. 以策略模式的形式准备一个 折扣记录
const calcPrice = (function () {

// 折扣列表
const calcList = {
'80%': function (total) { return (total * 0.8).toFixed(2) },
'70%': function (total) { return (total * 0.7).toFixed(2) }
}

function inner(total, type) {
if (!calcList[type]) return '0.00'

return calcList[type](total)
}

inner.add = function (type, fn) {
calcList[type] = fn
}

inner.remove = function (type) {
delete calcList[type]
}

inner.getList = function () {
return calcList
}

return inner
})()

// 0. 准备变量
let type = ''

// 1. 拿到当前所有的折扣渲染 li
// 将来一旦折扣添加了, 需要重新渲染 li
bindHtml()
function bindHtml() {
// 1-1. 拿到折扣类型列表
const list = calcPrice.getList()

// 1-2. 利用 list 渲染 li
let str = ''
for (let k in list) {
str += `
<li data-type="${ k }">${ k }</li>
`

}

ulBox.innerHTML = str
}


// 2. 折扣类型的选择(排他)
tab()
function tab() {
ulBox.addEventListener('click', e => {
if (e.target.nodeName !== 'LI') return

// 所有的没有类名
for (let i = 0; i < ulBox.children.length; i++) {
ulBox.children[i].classList.remove('active')
}

// 当前这个有类名
e.target.classList.add('active')

// 记录下当前的折扣类型
type = e.target.dataset.type
})
}

// 3. 结算按钮的事件
calcBtn.addEventListener('click', () => {
// 3-1. 拿到总价
const totalPrice = totalInp.value - 0

// 3-2. 计算最终价格
const resultPrice = calcPrice(totalPrice, type)

// 3-3. 把最终价渲染
pBox.innerText = resultPrice
})

// 4. 添加折扣
// 我们只能期望用户给我们两个内容
// 折扣名称
// 计算方式
addBtn.addEventListener('click', () => {

// 4-1. 拿到折扣名称
const name = nameInp.value

// 4-2. 拿到公式
// 开始组装
const r = '(parseInt(' + methodInp.value + ')).toFixed(2)'

// 语法: eval(字符串)
// 作用: 把该字符串当做js代码来执行
// const res = eval(r)

// 开始添加了
calcPrice.add(name, x => eval(r))

// 4-3. 从新渲染一遍 li
bindHtml()

// 4-4. 把两个文本框清空
nameInp.value = ''
methodInp.value = ''
})
</script>
</body>
</html>


 

  模块模式

  ●要是设计模式中的一种

  ● 模块模式可以指定类想暴露的属性和方法,并且不会污染全局。采用闭包的形式

 // 定义个自执行函数
var Person = (function() {
// 定义一个变量
var name = 'rose'
// 定义一个函数(方法)
function sayName() {
console.log('我的名字是:',name)
}
// 返回一个对象
// 因为是自执行函数 , Person得到的就是一个对象
// return {
// name: name,
// sayName: sayName
// }
return {
name,
sayName
}
})()
// 使用
console.log(Person);
Person.sayName()

 

声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。

猜你喜欢LIKE

Redis的八大特性有哪些?

2023-05-11

Redis应用场景有哪些?

2023-05-11

JavaScript全解析——DOM操作-获取元素的方式

2023-05-09

最新文章NEW

hooks模仿componentDidMount是什么

2023-05-18

JavaScript全解析——数据操作是什么

2023-05-11

createSelector的使用场景有哪些?

2023-04-25

相关推荐HOT

更多>>

快速通道 更多>>

最新开班信息 更多>>

网友热搜 更多>>