近日,重新学习梳理了JS设计模式,特地在这里做下笔记记录下。
工厂模式
特点
简单一句话概括 就是 return 一个 new 实例
应用场景:
- jQuery 创建
- React.createElement();
- vue.component();
单例模式
特点
- 单例模式 用到了 Java 中的 private 特性
代码
class SingleObject {
login() {
console.log("login...");
}
}
SingleObject.getInstance = (function() {
let instance;
return function() {
if (!instance) {
instance = new SingleObject();
}
return instance;
};
})();
let obj1 = SingleObject.getInstance();
obj1.login();
let obj2 = SingleObject.getInstance();
obj2.login();
console.log("obj1===obj2", obj1 === obj2);
console.log("分割线");
let obj3 = new SingleObject();
obj3.login();
console.log("obj1===obj3", obj1 === obj3);
应用场景
1、 jQuery $ 只有一个
if (window.jQuery !== null) {
return window.jQuery;
} else {
// 进行初始化操作
}
2、 登录框
class LoginForm {
constructor() {
this.state = "hide";
}
show() {
if (this.state === "show") {
alert("已经显示");
return;
}
this.state = "show";
console.log("登录框已经显示");
}
hide() {
if (this.state === "hide") {
alert("已经隐藏");
return;
}
this.state = "hide";
console.log("登录框已经隐藏");
}
}
LoginForm.getInstance = (function() {
let instance;
return function() {
if (!instance) {
instance = new LoginForm();
}
return instance;
};
})();
let login1 = LoginForm.getInstance();
login1.show();
let login2 = LoginForm.getInstance();
login2.show();
console.log("login1===login2", login1 === login2);
3、 vuex 和 redux 中的 store state 是单例模式
适配器模式
特点
- 旧接口格式 和 使用者不兼容
- 中间加一个适配器转换接口
代码
class Adaptee {
specificRequest() {
return "德国标准插头";
}
}
class Target {
constructor() {
this.adaptee = new Adaptee();
}
request() {
let info = this.adaptee.specificRequest();
return `${info} - 转换器 - 中国标准插头`;
}
}
let target = new Target();
let res = target.request();
console.log(res);
应用场景
1、 封装旧接口
// 自己封装的ajax 如下
ajax({
url: "/getData",
type: "POST",
dataType: "json",
data: {
id: "123"
}
}).done(function() {});
// 但因为历史原因,代码中全是;
// $.ajax({...})
// 做一层适配器
var $ = {
ajax: function(option) {
return ajax(option);
}
};
2、Vue computed 场景
Origin Message:{{message}}
Computed Message:{{reversedMessage}}
var vm = new Vue({
el: "#example",
data: {
message: "Hello"
},
computed: {
// 计算属性的 getter
reversedMessage: function() {
// `this`指向vm实例
return this.message
.split("")
.reverse()
.join("");
}
}
});
装饰器模式
特点
- 为对象添加新功能
- 不改变其原有的结构和功能
- 装饰器是一个函数
代码
// 基础代码
@decorator
class A {}
// 等同于
class A {}
a = decorator(A) || A;
// 装饰器传参 加参数
function testDesc(isDec) {
return function(target) {
target.isDec = isDec;
};
}
@testDesc(true)
class Demo {
//...
}
alert(Demo.isDec);
应用场景
1、 ES7 装饰器
- 配置环境-验证装饰器环境是否成功
@testDesc
class Demo {}
function testDesc(target) {
target.isDec = true;
}
alert(Demo.isDec);
- 可以装饰 class类
//1、mixin 实例
function mixins(...list) {
return function(target) {
Object.assign(target.prototype, ...list);
};
}
const Foo = {
foo() {
alert("foo");
}
};
@mixins(Foo)
class MyClass {}
let obj = new MyClass();
obj.foo();
- 可以装饰 方法function
// 例 1
function readonly(target, name, descriptor) {
//descriptor 是描述对象 (Object.defineProperty中会用到),原来的值如下
//{
// value:specifiedFunction,
// enumerable:false,
// configurable:true,
// writable:true
//}
descriptor.writable = false;
return descriptor;
}
class Person {
constructor() {
this.first = "A";
this.last = "B";
}
// 装饰方法
@readonly
name() {
return `${this.first} ${this.last}`;
}
}
var p = new Person();
console.log(p.name());
//p.name=function(){} // 这里会报错,因为name 是只读属性
2、例 2
function log(target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = function() {
console.log(`Calling ${name} with`, arguments);
return oldValue.apply(this, arguments);
};
return descriptor;
}
class Math {
//装饰方法
@log
add(a, b) {
return a + b;
}
}
const math = new Math();
const result = math.add(2, 4);
console.log("result", result);
- core-decorators
第三方类库
代理模式
特点
- 使用者无权访问目标对象
- 中间加代理,通过代理做授权和控制
代码
class ReadImg {
constructor(fileName) {
this.fileName = fileName;
this.loadFromDisk();
}
display() {
console.log(`display...` + this.fileName);
}
loadFromDisk() {
console.log(`load...` + this.fileName);
}
}
class ProxyImg {
constructor() {
this.realImg = new ReadImg();
}
display() {
this.realImg.display();
}
}
let proxyImg = new ProxyImg("1.png");
proxyImg.display();
应用场景
1、javascript 点击事件
2、明星经纪人
// 明星
let star = {
name: "刘德华",
age: 50,
phone: "15987452635"
};
// 经纪人
let agent = new Proxy(star, {
get: function(target, key) {
if (key === "phone") {
//返回经纪人自己手机号
return "15911111111";
}
if (key === "price") {
// 明星不报价,经纪人报价
return 120000;
}
return target[key];
},
set: function(target, key, value) {
if (key === "customPrice") {
if (val < 100000) {
// 最低10万
throw new Error("价格太低");
} else {
target[key] = value;
return true;
}
}
}
});
console.log(agent.name);
console.log(agent.age);
console.log(agent.phone);
console.log(agent.price);
agent.customPrice = 90000;
console.log("agent.customPrice", agent.customPrice);
代理模式 和 适配器模式 对比
- 适配器模式:提供一个不同的接口(如不同版本的接口)
- 代理模式:提供一模一样的接口
代理模式 和 装饰器模式 对比
- 装饰器模式:扩展原有功能,原有功能不变且可直接使用
- 代理模式:显示原有功能,但是经过限制或者阉割之后的
外观模式
特点
- 为子系统中的一组接口提供了一个高层接口
- 使用者使用这个高层接口
代码
function bindEvent(ele, type, selector, fn) {
if (fn === null) {
fn = selector;
selector = null;
}
//...
}
// 调用
bindEvent(elem, "click", "#div1", fn);
bindEvent(elem, "click", fn);
观察者模式
特点
- 发布订阅
- 一对多
- 被动接收,非主动
代码
class Subject {
constructor() {
this.state = 0;
this.observers = [];
}
getState() {
return this.state;
}
setState(state) {
this.state = state;
this.notifyAllObservers();
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update();
});
}
attach(observer) {
this.observer.push(observer);
}
}
// 观察者模式
class Observer {
constructor(name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this);
}
update() {
console.log(`${this.name} update state:${this.subject.getState()}`);
}
}
// 测试
let s = new Subject();
let o1 = new Observer("o1", s);
let o2 = new Observer("o2", s);
let o3 = new Observer("o3", s);
s.setState(1);
应用场景
1、网页绑定事件
2、Promise
function loadImg() {
var promise = new Promise(function(resolve, reject) {
var img = document.createElement("img");
img.onload = function() {
resolve(img);
};
img.onerror = function() {
reject("图片加载失败");
};
img.src = src;
});
return promise;
}
var src = "https://www.baidu.com/static/new.png";
var result = loadImg(src);
result
.then(function(img) {
console.log("width", img.width);
return img;
})
.then(function(img) {
console.log("height", img.height);
});
3、jQuery callbacks
var callbacks = $.Callbacks();
callbacks.add(function(info) {
console.log("fn1", info);
});
callbacks.add(function(info) {
console.log("fn2", info);
});
callbacks.add(function(info) {
console.log("fn3", info);
});
callbacks.fire("gogogo");
callbacks.fire("fire");
4、nodejs 自定义事件
(1) EventEmitter
const EventEmitter = require("events").EventEmitter;
const emitter1 = new EventEmitter();
emitter1.on("some", function() {
// 监听some
console.log("some event is occured 1");
});
emitter1.on("some", function() {
// 监听some
console.log("some event is occured 2");
});
emitter1.emit("some");
(2) 基于(1)的应用
const EventEmitter = require("events").EventEmitter;
// 继承
class Dog extends EventEmitter {
constructor(name) {
super();
this.name = name;
}
}
let simon = new Dog("simon");
simon.on("bark", function() {
console.log(this.name, " barked-1");
});
simon.on("bark", function() {
console.log(this.name, " barked-2");
});
setInterval(function() {
simon.emit("bark");
}, 1000);
(3) 基于(1)的应用
//Stream 用到了自定义事件
var fs = require("fs");
var readStream = fs.createReadStream("./data/file1.txt");
var length = 0;
readStream.on("data", function(chunk) {
length += chunk.toString().length;
});
readSteam.on("end", function() {
console.log(length);
});
(4) 基于(1)的应用
var readline = require("readline");
var fs = require("fs");
var rl = readline.createInterface({
input: fs.createReadStream("./data/file1.txt")
});
var lineNum = 0;
rl.on("line", function(line) {
lineNum++;
});
rl.on("close", function() {
console.log("lineNum", lineNum);
});
(5)nodejs 中:处理 http 请求;多进程通讯
function serverCallback(req, res) {
var method = req.method.toLowerCase(); // 获取请求的方法
if (method === "get") {
// 省略三行,上文示例代码中处理GET请求的代码
}
if (method === "post") {
// 接收post请求的内容
var data = "";
req.on("data", function(chunk) {
// 一点点 接收内容
data += chunk.toString();
});
req.on("end", function() {
//
res.writeHead(200, { "Content-Type": "text/html" });
res.write(data);
res.end();
});
}
}
//parent.js
var cp = require("child-process");
var n = cp.for("./sub.js");
n.on("message", function(m) {
console.log("Parent got message: " + m);
});
n.send({ hello: "world" });
//sub.js
process.on("message", function(m) {
console.log("child got message: " + m);
});
process.send({ foo: "bar" });
(6)vue 和 react 组件生命周期触发
(7)vue watch