1、构造器模式
原始写法
const person1 = {
name:'zyf',
age:10
}
const person2 = {
name:'pp',
age:20
}
....
这些对象的模板都一样,有没有什么简单一点的写法呢?
构造器写法
function Person(name,age){
this.name = name
this.age = age
}
const person1 = new Person('zyf','10')
const person2 = new Person('ls','20')
2、原型模式
构造器写法
function Person(name,age){
this.name = name
this.age = age
this.info = function(){
console.log(this.name, this.age)
}
}
const person1 = new Person("zyf", "10");
const person2 = new Person("ls", "20");
person1.info();
person2.info();
构造器模式有一个很明显的缺点,那就是在new一个对象时,如果里面存在有方法,这个方法就会反复的创建,这样会导致内存的冗余
原型写法
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.info = function () {
console.log(this.name, this.age);
};
const person1 = new Person("zyf", "10");
const person2 = new Person("ls", "20");
person1.info();
person2.info();
可以发现方法从始至终只创建了一次,且功能也完全得以实现
3、工厂模式
设计一个权限导航页面,根据用户角色,来决定展示什么页面
构造器模式
funtion User(role,page){
this.role = role
this.page = page
}
const user1 = new User('customer',['login','home','shopping'] )
const user2 = new User('admin',['login','home','shopping','manage'] )
const user3 = new User('customer',['login','home','shopping'] )
const user4 = new User('customer',['login','home','shopping'] )
...
会发现当创建用户时,后面的页面内容很多余,创建一百个用户时,难道页面的代码也要写一百遍吗?
工厂写法
function Factor(role) {
function User(role, page) {
this.role = role;
this.page = page;
}
switch (role) {
case "admin":
return new User("admin", [
"login",
"home",
"shopping",
"manage",
]);
break;
case "superAdmin":
return new User("superAdmin", [
"login",
"home",
"shopping",
"manage",
"add",
"delete",
]);
default:
return new User("customer", [
"login",
"home",
"shopping",
]);
}
}
const user1 = new Factor("customer");
const user2 = new Factor("admin");
console.log(user1);
console.log(user2);
4、抽象工厂模式
普通工厂返回的是一个实例对象,抽象工厂返回的是一个类,这样可以根据不同的角色类型,来写一些逻辑并统一进行封装
抽象工厂写法
需要用到寄生组合继承的知识
function User(name, role, pages) {
this.name = name;
this.role = role;
this.pages = pages;
}
User.prototype.welcome = function () {
console.log("欢迎回来", this.name);
};
User.prototype.data = function () {
throw new Error("抽象方法需要被实现");
};
function SuperAdmin(name) {
User.call(this, name, "superAdmin", [
"login",
"home",
"shopping",
"manage",
"add",
"delete",
]);
}
SuperAdmin.prototype = Object.create(User);
// 权限功能
SuperAdmin.prototype.dataShow = function () {
console.log("superadmin-datashow");
};
SuperAdmin.prototype.addRight = function () {};
function Editor(name) {
User.call(this, name, "editor", ["home", "shopping", "manage"]);
}
Editor.prototype = Object.create(User);
Editor.prototype = User.prototype;
// 权限功能
Editor.prototype.dataShow = function () {
console.log("editor-datashow");
};
function getAbstractUserFactor(role) {
switch (role) {
case "superAdmin":
return SuperAdmin;
case "editor":
return Editor;
default:
return null;
}
}
const editorClass = getAbstractUserFactor("editor");
const user = new editorClass("zyf");
user.welcome();
可以用es6的class写法,结构看起来也比较清晰
class User {
constructor(name, role, pages) {
this.name = name;
this.role = role;
this.pages = pages;
}
welcome() {
console.log("欢迎回来", this.name);
}
data() {
throw new Error("抽象方法需要被实现");
}
}
class SuperAdmin extends User {
constructor(name) {
super(name, "superAdmin", [
"login",
"home",
"shopping",
"manage",
"add",
"delete",
]);
}
dataShow() {
console.log("superadmin-datashow");
}
addRight() {}
}
class Editor extends User {
constructor(name) {
super(name, "editor", ["home", "shopping", "manage"]);
}
dataShow() {
console.log("editor-datashow");
};
}
class getAbstractUserFactor {
constructor(role) {
switch (role) {
case "superAdmin":
return SuperAdmin;
case "editor":
return Editor;
default:
return null;
}
}
}
const editorClass = new getAbstractUserFactor("editor");
const user = new editorClass("zyf");
user.welcome();
5、建造者模式
- 建造者模式属于创建模式的一种,提供一种创建复杂对象的方式,它将一个复杂的对象构建与它的表示分离,使得同样的构建过程可以创建不同的表示
- 建造者模式是一步一步的创建一个复杂的对象,它允许用户只通过指定复杂的对象的类型和内容就可以构建它们,用户不需要指定内部的具体构造细节
原始写法
class Cat
init(){
console.log('喵喵喵');
}
getDate(){
console.log('爱吃鱼');
}
render(){
console.log('可爱捏');
}
}
class Dog{
init(){
console.log('汪汪汪');
}
getDate(){
console.log('爱吃肉');
}
render(){
console.log('忠诚捏');
}
}
const cat = new Cat()
cat.init()
cat.getDate()
cat.render()
const dog = new Dog()
dog.init()
dog.getDate()
dog.render()
每个方法都得挨个去创建
建造者写法
class Cat {
init() {
console.log("喵喵喵");
}
getDate() {
console.log("爱吃鱼");
}
render() {
console.log("可爱捏");
}
}
class Dog {
init() {
console.log("汪汪汪");
}
getDate() {
console.log("爱吃肉");
}
render() {
console.log("忠诚捏");
}
}
class Animal {
create(param) {
param.init();
param.getDate();
param.render();
}
}
const animal = new Animal();
animal.create(new Cat());
animal.create(new Dog());
6、单例模式
- 利用闭包的不可回收机制来实现
- 一个类仅有一个实例,并提供一个访问它的全局访问点
- 主要解决一个全局使用的类频繁的创建和销毁,占用内存
简单的单例实现
const single = (function () {
let instance;
function User(name, age) {
this.name = name;
this.age = age;
}
return function (name, age) {
if (!instance) {
instance = new User(name, age);
}
return instance;
};
})();
单例的应用场景:
- redux 、vuex,这些技术都是基于,单例的每次打开的都是同样的实例这一性质来实现的
- 对话框,每次点开好友的对话框都是同一个对话框,上面还有聊天记录
简单的场景实现
<!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>
<style>
.kerModal {
position: fixed;
width: 200px;
height: 200px;
line-height: 200px;
text-align: center;
left: 50%;
top: 30%;
transform: translate(-50%, -50%);
background-color: rgb(33, 172, 214);
display: none;
}
</style>
</head>
<body>
<button id="open">打开</button>
<button id="close">关闭</button>
<script>
const Modal = (function () {
let instance = null;
return function () {
if (!instance) {
instance = document.createElement("div");
instance.innerHTML = "登录对话框";
instance.className = "kerModal";
instance.style.display = "none";
document.body.appendChild(instance);
}
return instance;
};
})();
document.querySelector("#open").onclick = function () {
// 创建modal
const modal = Modal();
// 显示modal
modal.style.display = "block";
};
document.querySelector("#close").onclick = function () {
// 创建modal,每次创建调用函数时,都会进行里面的if判断,若已经创建,则沿用之前的实例
const modal = Modal();
// 关闭modal
modal.style.display = "none";
};
</script>
</body>
</html>
7、 装饰器模式
装饰器模式能够很好的对已有功能进行拓展,这样不会更改原有的代码,对其他的业务产生影响,这方便我们在较少的改动下对软件功能进行拓展
简单的装饰器模式写法
Function.prototype.before = function (beforeFun) {
const _this = this;
return function () {
beforeFun.apply(this, arguments);
return _this.apply(this, arguments);
};
};
Function.prototype.after = function (afterFn) {
const _this = this;
return function () {
const ret = _this.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
};
};
function test() {
console.log(22222);
return 44444;
}
const test1 = test
.before(() => {
// debugger;
console.log(11111);
})
.after(() => {
console.log(33333);
});
console.log(test1());
输出结果为
console.log打印输出统一在这里运行
装饰器模式类似于一个可拔插的接口工具,可对前后的业务逻辑进行处理
简单的装饰器模式实践
<button id="filmbtn">点击</button>
<script>
Function.prototype.before = function (beforeFun) {
const _this = this;
return function () {
beforeFun.apply(this, arguments);
return _this.apply(this, arguments);
};
};
Function.prototype.after = function (afterFn) {
const _this = this;
return function () {
const ret = _this.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
};
};
function log() {
console.log("上传uv,pv数据");
}
function render() {
console.log("页面处理逻辑");
}
render = render.before(log);
filmbtn.onclick = function () {
render();
};
</script>
运行结果
可以同步或异步的控制输出顺序
8、适配器模式
将一个类的接口转换成客户希望的另一个接口。适配器能让那些接口不兼容的类一起工作
实现一个统一运行接口的方法renderMap
普通写法
class TecentMap {
show() {
console.log("开始渲染腾讯地图");
}
}
class BaiduMap {
display() {
console.log("开始渲染百度地图");
}
}
function renderMap(map) {
map.display?.() || map.show?.();
}
renderMap(new TecentMap());
renderMap(new BaiduMap());
可以看到,方法renderMap里还需要加判断语句,来对各种接口进行判断
适配器写法
class TecentMap {
show() {
console.log("开始渲染腾讯地图");
}
}
class BaiduMap {
display() {
console.log("开始渲染百度地图");
}
}
/*
* 对TecentMap接口进行适配,在外面包一层display的方法名,来和baiduMap的display方法名保持一致
*/
class TencentAdapater extends TecentMap {
constructor() {
super();
}
display() {
this.show();
}
}
// 统一执行display方法
function renderMap(map) {
map.display();
}
renderMap(new TencentAdapater());
renderMap(new BaiduMap());
9、策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互转换,且算法的变化不会影响使用算法的客户。策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分隔开来,并委派给不同的对象对这些算法进行管理。
该模式主要解决在有多种算法相似的情况下,使用if...else所带来的复杂和难以维护。它的优点是算法可以自由切换,同时可以避免多重if...else判断,且具有良好的扩展性
普通写法
function calBonus(level, salary) {
if (level === "A") {
return salary * 4;
}
if (level === "B") {
return salary * 5;
}
if (level === "C") {
return salary * 6;
}
}
calBonus("A", 10000);
calBonus("B", 100);
策略写法
const strategry = {
A: (salary) => salary * 4,
B: (salary) => salary * 5,
C: (salary) => salary * 6,
};
function calBonus(level, salary) {
return strategry[level](salary);
}
calBonus("A", 10000);
calBonus("B", 100);
10、代理模式
代理模式,为其它对象提供一种代理以控制对这个对象的访问
代理模式使得代理对象控制具体对象的引用,代理几乎可以是任何对象:文件,资源内存中的对象,或者是一些难以复制的对象
类似于明星接戏,都有一个经纪人的
class Star {
play() {
console.log("演戏");
}
}
// 经纪人
class StarProxy {
constructor() {
this.superStar = new Star();
}
talk(price) {
if (price >= 10000) {
this.superStar.play();
} else {
console.log("价钱不合适");
}
}
}
const jr = new StarProxy();
jr.talk(1000);
jr.talk(10000);
打印结果
ES13中的代理proxy
const star = {
name: "tiechui",
workPrice: 10000,
};
const proxy = new Proxy(star, {
get(target, key) {
if (key === "workPrice") {
return "已经访问了";
}
},
set(target, key, value) {
if (key === "workPrice") {
console.log("设置了");
if (value > 10000) {
console.log("可以合作");
} else {
throw error("价钱不合适");
}
}
},
});
打印结果
11、观察者模式
观察者模式包含观察目标和观察者两类对象
一个目标可以有任意数目的与之相依赖的观察者
一旦观察者目标的状态发生改变,所有的观察者都将得到通知
当一个对象的状态发生改变时,所有的依赖于它的对象都得到通知并被自动更新,解决了主题对象与观察者之间功能的耦合,即一个对象状态改变和其他对象的通知的问题
观察者写法
class Subject {
constructor() {
this.observe = [];
}
add(observe) {
this.observe.push(observe);
}
remove(observe) {
this.observe = this.observe.filter(
(item) => item !== observe
);
}
notify() {
this.observe.forEach((item) => {
item.update();
});
}
}
class Observer {
constructor(name) {
this.name = name;
}
update() {
console.log("我叫", this.name);
}
}
const subject = new Subject();
const observe1 = new Observer("zyf");
const observe2 = new Observer("ls");
subject.add(observe1);
subject.add(observe2);
subject.remove(observe1);
subject.notify();
应用场景
- 面包屑
- rxjs
- ...
面包屑案例编写
<!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>
<style>
.box {
display: flex;
height: 600px;
}
.box .left {
width: 150px;
background-color: yellow;
}
.box .right {
flex: 1;
background-color: aqua;
}
</style>
<body>
<header class="header"></header>
<div class="box">
<div class="left">
<ul>
<li>首页</li>
<li>用户管理</li>
<li>权限管理</li>
<li>新闻管理</li>
</ul>
</div>
<div class="right">
<div class="bread"></div>
</div>
</div>
<script>
class Subject {
constructor() {
this.observe = [];
}
add(observe) {
this.observe.push(observe);
}
remove(observe) {
this.observe = this.observe.filter(
(item) => item !== observe
);
}
notify(data) {
this.observe.forEach((item) => {
item.update(data);
});
}
}
class Observer {
constructor(element) {
this.element = document.querySelector(element);
}
update(data) {
this.element.innerHTML = data;
}
}
const subject = new Subject();
const observe1 = new Observer(".bread");
const observe2 = new Observer(".header");
let oli = document.querySelectorAll(".left li");
for (let i = 0; i < oli.length; i++) {
oli[i].onclick = function () {
subject.notify(this.innerHTML);
};
}
subject.add(observe1);
subject.add(observe2);
</script>
</body>
</html>
当点击左侧的导航栏时,右侧以及上面会出现点击的导航栏信息
12、发布订阅模式
观察者和目标要相互知道
发布者和订阅者不用相互知道,通过第三方实现调度,属于经过解耦合的观察者模式
发布订阅简洁版
const PubSub = {
list: [],
publish() {
this.list.forEach((item) => item());
},
subscript(cb) {
this.list.push(cb);
},
};
function testA() {
console.log("testA");
}
function testB() {
console.log("testB");
}
PubSub.subscript(testA);
PubSub.subscript(testB);
PubSub.publish();