注:部分代码及名词摘自《Javascript设计模式》,《设计模式:可复用面向对象的软件基础》
什么是设计模式?
设计模式是解决程序设计中的常见问题的可复用方案,是编写出美观、结构化和组织良好代码代码的前提,其三法则为适合性、实用性、适用性,以下代码均采用ES6语法
一 、模块模式
对象拥有共有/私有方法和变量,屏蔽全局作用域中特殊的部分,降低冲突
const testModule = (function() {
// 私有变量
let count = 0
// 私有函数
const privateFn = () => {
console.log("log...")
}
// 公有属性和方法,通过一个立即执行函数闭合代码 return 公有API
return {
type: "num: ",
Fn: privateFn // 私有函数的公有形式别名
add: () => ++count,
get: () => {
// 执行私有方法并返回私有变量
return privateFn(), count
}
}
})()
testModule.add()
console.log(testModule.type + testModule.get()) // -> num: 1
只有模块才能享受私有函数,可执行UA测试,针对不同情况例如兼容IE只需提供一个对应的代码路径
增强:Mixed(引入混出)
const testModule = (function(_JQ) {
_JQ.fn.extend({
alertWhileClick: function () {
$(this).click(function () {
alert($(this).val());
});
}
});
return _JQ
})(jQuery)
改进:揭示模块模式
const testModule = (function() {
let privateName = "yun"
publicSex = "Ms."
function publicGetName(){
privateFun()
}
function privateFun(){
return privateName
}
function publicSetName(sName){
privateName = sName
}
// 将暴露的公有指针指向到私有函数和属性上
return {
get: publicGetName,
set: publicSetName,
greeting: publicSex
}
})()
封装更加简洁,并且支持私有数据,在模块中代码的公有部分能访问私有部分,外界则无法接触类的私有部分。
二、单例模式
限制类的实例化次数只能为一,已存在的情况下简单返回对对象的引用
const mySingleton = (function() {
// 保存对象的引用
let instance;
function init() {
// 私有属性及方法
let privateNum = Math.random()
const privateFun = () => privateNum
// 对外暴露的公有属性及方法
return {
type: "Number",
getNum: privateFun
}
}
return {
// 获取判断,不存在则初始化赋值,存在即简单返回引用
getInstance: () => {
if(!instance){
instance = init()
}
return instance
}
}
})()
const singleA = mySingleton.getInstance()
const singleB = mySingleton.getInstance()
// 判断两次值为一样则成功
console.log(singleA .getNum() == singleB.getNum()) // -> true
延迟构建:协调对象
const mySingleton = (function() {
// 模拟初始化
function Penson(options) {
options = options || {}
this.name = "yun"
this.friend = options.friend || "liu"
}
let instance;
// 静态属性
let _static = {
type: "Object",
getInstance: (options) => {
if(!instance){
instance = new Penson(options)
}
return instance
}
}
return _static
})()
const singleA = mySingleton.getInstance({
friend: "touda"
})
console.log(singleA.friend) // -> touda
需要一个对象来协调其他对象,加强紧密耦合
二、观察者模式
一个对象维持一系列依赖于它的对象,负责通知任何变更的状态
-
Subjie (目标)
-
Observer (观察者)
-
ConcreteSubject (具体目标)
-
ConcreteObserver (具体观察者)
/*
* 1. 模拟一个目标可能拥有的一系列依赖Observer
* 2. 模拟目标在观察者列表上添加、删除、通知的能力
* 3. 定义一个框架来创建新的Observer
*/
// 观察者列表
function ObserverList() {
this.list = []
}
// 行为列表
ObserverList.prototype.add = function(value) {
this.list.push(value)
}
ObserverList.prototype.get = function(index) {
return this.list[index]
}
ObserverList.prototype.count = function() {
return this.list.length
}
// 使用extension扩展对象
function extend(object, extension) {
for (const key in object) {
extension[key] = object[key]
}
}
// 为对象添加属性形成链式调用并分隔原型方法
function Observer() {
// 分配行为
this.update = function () {
// ...
}
}
// 目标
function Subject(){
this.observers = new ObserverList()
}
Subject.prototype.addObserver = function(oElm) {
this.observers.add(oElm)
}
Subject.prototype.Notify = function(context) {
for (let index = 0; index < this.observers.count(); index++) {
this.observers.get(index).update(context) //获取具体观察者执行update属性方法
}
}
<button id="add">Add</button>
<input type="checkbox" id="control">
<label for="control">全选</label>
<div id="container"></div>
let checkbox = document.getElementById("control"),
container = document.getElementById("container"),
addBtn = document.getElementById("add");
// 利用Subject扩展checkbox为目标
extend(new Subject(), checkbox)
// 通知到观察者
checkbox.onclick = ({target}) => {
checkbox.Notify(target.checked)
}
addBtn.onclick = addNewObserver
function addNewObserver() {
// 创建新的checkbox
let check = document.createElement("input")
check.type = "checkbox"
// 利用Observer扩展checkbox
extend(new Observer(), check)
// 自定义更新行为
check.update = function(value){
this.checked = value
}
// 添加新的观察者并附件到容器
checkbox.addObserver(check)
container.appendChild(check)
}
.....未完待续