设计模式
什么是设计模式
面向对象开发人员在长期开发过程中总结的一套解决特定问方案写法
设计模式分类
创建型模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
结构型模式
这些设计模式关注类和对象的组合,继承的概念被用来组合接口和定义组合对象获得新功能的方式。
行为型模式
这些设计模式特别关注对象之间的通信。
单例模式
这种模式确保整个应用中只有单个对象(实例)存在。
lass Person {
// 创建对象之前先判断它是否存在,如果存在,直接使用,不存在去创建
// static修饰的方法称为静态方法,直接通过类名调用与实例对象无关
static getInstance() {
if (this.instance == null) {
this.instance = new Person()
}
return this.instance
}
constructor() {
this.name = 'jack'
}
}
// 创建对象之前先判断它是否存在,如果存在,直接使用,不存在去创建
// let p1 = new Person() // jack
// let p2 = new Person() // jack
let p1 = Person.getInstance()
let p2 = Person.getInstance()
console.log(p1 === p2)
单例模式示例:创建一个节点对象
// 面向对象方式创建div节点
class CreateDiv{
static getInstance(){
if(this.instance == null){
this.instance = new CreateDiv()
}
return this.instance
}
constructor(){
this.divEle = document.createElement('div') // 创建div节点
document.body.appendChild(this.divEle) // 添加div到body节点
}
init(context){
this.divEle.innerHTML = context
}
}
let div1 = CreateDiv.getInstance() // new CreateDiv()
div1.init('hello')
let div2 = CreateDiv.getInstance() // new CreateDiv()
div2.init('world')
// 希望只有一个div实例存在,每次执行init方法都给同一个div实例对象复制
// 单例模式,保存整个应用程序中只有一个实例存在
观察者模式
研究对象与对象之间关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新
例子:
- 明星 与 粉丝
- 张三 与 小王、 小李、 小丽
- 张三状态发生变化 它的粉丝都可收到通知
// 明星类 被观察者-主体
class Star{
constructor(name) {
this.name = name
this.list = [] // 存储粉丝对象
}
//添加粉丝对象
add(obj){
this.list.push(obj)
}
//更新状态
updateState(message){
this.list.forEach(obj=>{
obj.receive(message)
})
}
}
// 粉丝类,观察者
class Observer{
constructor(name) {
this.name = name
}
// 接收信息
receive(msg){
console.log(this.name + '看到信息 '+ msg + ' 很伤心!')
}
}
// 创建明星李易峰,创建粉丝,小丽,小红,小张
// 李易峰宣布“离开娱乐圈”
let lyf = new Star('李易峰')
let xiaoli = new Observer('小丽')
let xiaoRed = new Observer('小红')
let xiaozhang = new Observer('小张')
//关注明星
lyf.add(xiaoli)
lyf.add(xiaoRed)
lyf.add(xiaozhang)
// 李易峰宣布“离开娱乐圈”
lyf.updateState('李易峰离开娱乐圈')
发布订阅模式
发布者 -> 消息事件中心 -> 订阅者
不同类型 -> 订阅者
按钮点击事件:
发布者(点击按钮的人) -- 事件处理中心 -- 订阅者(按钮)
三部份组成 :
发布者 : 负责发布消息事件中心
订阅者 : 订阅事件处理中心消息
事件处理中心 :
处理不同类型的消息,发送给不同的订阅者
{
消息类型: [消息事件1,消息事件2]
click: [执行订阅的消息1,执行订阅的消息2],
mousemove:[...]
}
订阅消息:向事件对象添加一条数据
发布消息:触发指定类型消息
/*购书
小明去书店购书,书店没有三国演义本书,小明订阅这本书,当书到达时通知我
小红啊去书店购书,书店没有西游记本书,小明订阅这本书,当书到达时通知我
订阅者 事件处理中心 发布者(出版商,书店)
购书buybook
通知订阅者书到达 */
// 事件处理中心
class EventCenter{
constructor(){
//{type:[执行消息处理函数1,执行消息处理函数2...]}
this.subs = {}
}
//订阅消息
addEvent(type,even){
// if(!this.subs[type]){
// this.subs[type] = []
// }
this.subs[type] = this.subs[type] || []
this.subs[type].push(even)
}
// 发布消息
emitEvent(type){
if(this.subs[type]){
this.subs[type].forEach(even=>{
even()
})
}
}
}
let eventCenter = new EventCenter()
eventCenter.addEvent('buybook',function(){
console.log('你订阅的三国演义书已到');
})
eventCenter.addEvent('buybook',function(){
console.log('你订阅的西游记书已到');
})
eventCenter.emitEvent('buybook')
<button class="btn1">订阅者1</button>
<button class="btn2">订阅者2</button>
<script>
function test1() {
const btn1 = document.querySelector('.btn1')
const btn2 = document.querySelector('.btn2')
btn1.addEventListener('click', function () {
console.log('执行订阅的消息1')
})
btn2.addEventListener('click', function () {
console.log('执行订阅的消息2')
})
} //未使用发布订阅模式的代码
</script>
发布订阅模式与观察者区别
观察者模式是由具体目标调度,观察者模式的订阅者与发布者之间是存在依赖的。
发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。
垃圾回收机制
回收释放不在使用的"对象"占用的内存空间
内存管理:
- 分配内存
- 读写内存
- 回收释放内存
回收释放内存
- c语言中 malloc()和free()作用回收释放内存的方法
- javascript语言中由"垃圾回收"程序自动处理回收释放内存
function test1() { let obj = { name: 'jack', age: 18 } obj.name = 'rose' obj.age = 20 // malloc() free() 回收释放内容 // obj = null }
垃圾回收机制
垃圾回收算法
1. 引用计数垃圾收集
判断当前对象有无其它对象引用,如果没有,垃圾回收机制释放它占用的内存空间
(垃圾回收程序定时遍历内存中所有对象)
缺点:
不能回收相互引用的对象
2. 标记清法
垃圾回收器将定期从根开始,找所有从根开始引用的对象,如果没有引用,否被回收
购物车全选单选实现[单页应用]
<div class="containter">
<!--==== logo区域===== -->
<div class="logo">
<img src="./image/dd_logo.jpg" alt="logo" />
</div>
<!--==== 购物车列表区域==== -->
<table>
<!-- 动态渲染 -->
</table>
<!-- 所有商品总价 -->
<div>
<h2>总价:¥<span style="color: red;" class="total-price">0</span></h2>
</div>
<!-- 商品表单 -->
<div class="prowraper">
<form>
<input type="text" name="name" placeholder="请输入商品名"><br/>
<input type="text" name="price" placeholder="请输入商品价格"> <br/>
<input type="text" name="num" placeholder="请输入商品数量"> <br/>
<input type="text" name="url" placeholder="请输入商品图片地址"> <br/>
<input type="submit" id="confirm" value="添加商品">
</form>
</div>
</div>
// 商品列表
let productList = [
{ number: 1001, name: 'javascript高级编程', url: './image/shoppingBg_03.jpg', price: 88.98, num: 0, state: false },
{ number: 1002, name: 'css高级编程', url: './image/shoppingBg_06.jpg', price: 58.58, num: 0, state: false },
{ number: 1003, name: 'html高级编程', url: './image/shoppingBg_03.jpg', price: 48.58, num: 0, state: false },
]
let stateAll = false // 全选状态
const tableEle = document.querySelector('table')
const totalPriceEle = document.querySelector('.total-price')
/*
* 显示商品列表
*/
function showProductList() {
let str = `<tr>
<th><input type="checkbox" name="checkall" ${stateAll ? 'checked' : ''} /></th>
<th>序列号</th>
<th>商品图片</th>
<th>商品信息</th>
<th>单价</th>
<th>数量</th>
<th width="100px">总价</th>
<th>操作</th>
</tr>`
let trArr = productList.map(item => {
return `<tr>
<td><input type="checkbox" name="checkSingle" ${item.state ? 'checked' : ''} data-id="${item.number}"/></td>
<td>${item.number}</td>
<td><img src="${item.url}" alt="图片1" /></td>
<td>${item.name}</td>
<td>¥${item.price}</td>
<td>
<input type="button" value="-" name="minus" ${item.num == 0 ? 'disabled' : ''} data-id="${
item.number
}"/><input type="text" value="${item.num}" name="amount"/><input
type="button"
value="+"
name="plus"
data-id="${item.number}"
/>
</td>
<td>¥${(item.price * item.num).toFixed(2)}</td>
<td>移入收藏<br /><a href="javascript:void(0)" class="del" data-id="${item.number}">删除</a></td>
</tr>`
})
str = str + trArr.join('')
tableEle.innerHTML = str
setTotalPrice() // 因为所有操作都会刷新商品列表,统一计算总价
}
/**
* 绑定添加商品事件
*/
function bindAddProduct() {
const formEle = document.querySelector('form')
const nameInput = document.querySelector('input[name="name"]')
const priceInput = document.querySelector('input[name="price"]')
const numInput = document.querySelector('input[name="num"]')
const urlInput = document.querySelector('input[name="url"]')
formEle.addEventListener('submit', function (e) {
e = e || window.event
e.preventDefault() // 阻止默认行为
let name = nameInput.value // 商品名称
let price = priceInput.value // 商品价格
let num = numInput.value // 商品数量
let url = urlInput.value // 商品地址
addProduct(name, price, num, url)
// 清空表单数据
nameInput.value = ''
priceInput.value = ''
numInput.value = ''
urlInput.value = ''
})
}
/**
* 添加商品
* 1. 构造商品对象
* let product = { number: 1004, name: 'vue高级编程', url: './image/shoppingBg_03.jpg', price: 108.58, num: 0 }
* 2. 添加商品到商品数组
* productList.push(product)
*/
function addProduct(name, price, num, url) {
let number = getRandom(1000, 10000) // 随机生成4位商品序号
// 1. 构造商品对象
let product = {
number,
name,
url,
price,
num,
}
productList.push(product) // 2. 添加商品到商品数组
showProductList() // 3. 重新加载商品数组
}
/*
* 返回m到n之间的随机数
*/
function getRandom(x, y) {
var n = Math.max(x, y)
var m = Math.min(x, y)
return Math.floor(Math.random() * (n - m) + m)
}
/**
* 绑定删除按钮事件
* 实现方式
* 事件属性
* 事件赋值
* 事件监听
* 事件委托- 动态添加的节点不需要重新绑定事件
*/
function bindDeleteProduct() {
tableEle.addEventListener('click', function (e) {
e = e || window.event
let target = e.target || e.srcElement //事件目标对象
// 判断是否删除操作
if (target.getAttribute('class') == 'del') {
let id = target.dataset.id
// 遍历数组查询数组中满足条件的元素的索引号
let index = productList.findIndex(item => item.number == id)
productList.splice(index, 1)
// 重新刷新商品列表
showProductList()
}
// 加操作
if (target.getAttribute('name') == 'plus') {
let id = target.dataset.id
let product = productList.find(item => item.number == id)
product.num++ //数量加一
showProductList()
}
// 减操作
if (target.getAttribute('name') == 'minus') {
let id = target.dataset.id
let product = productList.find(item => item.number == id)
product.num-- //数量减一
showProductList()
}
/**
* 全选操作
* 2.0操作数据
* 1. 设置全选状态stateAll = !stateAll
* 2. 设置所有复选框状态
*/
if (target.getAttribute('name') == 'checkall') {
stateAll = !stateAll // 1. 设置全选状态
// 2. 设置所有复选框状态
productList.forEach(product => {
product.state = stateAll
})
// 3. 更新界面
showProductList()
}
/**
* 单选框操作
* 1. 设置当前选中商品状态 state = !state
* 2. 设置全选状态 stateAll
* 所有单选框都选中是 stateAll选中
* state=true stateAll = true
* 只要有一个state为false stateAll = false
*/
if (target.getAttribute('name') == 'checkSingle') {
let id = target.dataset.id
// 1. 设置当前选中商品状态
let product = productList.find(item => item.number == id)
product.state = !product.state
// 2. 设置全选状态
stateAll = productList.every(item => item.state == true)
// 3. 刷新界面
showProductList()
}
})
}
/**
* 计算所有商品总价
* 选中商品计算总价
* state=true
*/
function getTotal() {
let s = productList.reduce(
(previous, current) => (previous = previous + (current.state ? current.price * current.num : 0)),
0
)
return s.toFixed(2)
}
/**
* 设置总价
*/
function setTotalPrice() {
let totalPrice = getTotal()
totalPriceEle.innerHTML = totalPrice
}
showProductList()
bindAddProduct()
bindDeleteProduct()
setTotalPrice()
* {
padding: 0;
margin: 0;
}
a{
text-decoration: none;
color: rgb(43, 42, 42);
}
.containter {
width: 1200px;
margin: 100px auto;
}
.containter table{
width: 100%;
}
.containter tr {
line-height: 40px;
text-align: center;
}
.containter tr,
th,
td {
border-bottom: 1px dotted gray;
}
.containter tr input {
width: 20px;
text-align: center;
}
.containter h2{
font-size: 18px;
text-align: right;
}
.containter table img{
width: 80px;
height: 100px;
}
/* 添加商品表单 */
.prowraper{
width: 600px;
margin: 20px auto;
border: 1px solid gray;
}
.prowraper form{
width: 100%;
padding: 20px;
}
.prowraper form input{
margin-top: 10px;
width: 90%;
line-height: 30px;
}
.prowraper form #confirm{
margin-top: 10px;
width: 200px;
height: 40px;
text-align: center;
line-height: 40px;
}