JavaScript 设计模式(一)

本文需要读者至少拥有基础的 ES6 知识,包括 Proxy, Reflect 以及 Generator 函数等。

至于这次为什么分了两篇文章,有损传统以及标题的正确性,是这样的。

其实放在一篇文章里也可以,但是希望读者能够更加轻松点,文章太长也会导致陷入阅读疲倦中。

因此希望读者理解。

1. 工厂模式

JavaScript 寄生模式就是一种 工厂模式,具体可以参考我的关于 JavaScript 继承这篇文章,这里不再细谈寄生模式。

工厂模式是用工厂方法代替 new 的一种设计模式。

先看一个工厂模式的具体例子

class Product {
constructor(name) {
this.name = name;
}
} class Factory {
static create(name) {
return new Product(name);
}
} Factory.create("product1");
Factory.create("product2");

通过这种设计模式,我们可以少写一个 new

jQuery 源码中,这种设计模式也有体现

$('#div'); // 我们会这样传入一个 selector 返回一个 jQuery.fn.init 对象

下面我们具体看源码中的内容,以下是我简化过的源码

function jQuery(selector) {
return new jQuery.fn.init(selector)
} jQuery.fn = jQuery.prototype; // 简化 原型方法 书写
jQuery.fn.eat = function() {
console.log(`${this.name} eat!`);
return this;
} const init = jQuery.fn.init = function(selector) {
this.name = selector;
} // 使得 jQuery.fn.init.prototype 与 jQuery.prototype 保持一致
// 用以使用 jQuery.prototype 即 jQuery.fn 上定义的方法或属性
init.prototype = jQuery.prototype; // 工厂模式
window.$ = function(selector) {
return new jQuery(selector);
} console.log($("huro").eat())

jQuery 实现的源码中,还是比较绕的,这种绕,其实这样隐隐约约的实现了组合寄生继承,分离了属性和方法。

因为这个时候属性例如 this.name 会在实例 new jQuery.fn.init() 上,

而这个实例的 __proto__ 指向 jQuery.prototype ,而我们是在 jQuery.prototype 上定义方法的,所以隐隐约约的,实现了属性的独立和方法的共享,节省了内存空间。

2. 单例模式

JavaScript 中没有很好的单例模式的实现,究其原因,是因为没有 private 关键字保护构造函数,现在最新的语法提案已经提出利用 # 字代表私有属性或方法,可能几年后就有了。如:

class Person {
#name // 代表是一个私有属性
}

目前单例模式我们一般这样实现

class Singleton {
eat() {
console.log("huro eat!");
}
} Singleton.getInstance = (() => {
let instance = null;
return () => {
if (instance === null) {
instance = new Singleton();
}
return instance;
};
})(); const obj1 = Singleton.getInstance();
const obj2 = Singleton.getInstance();
console.log(obj1 === obj2);
obj1.eat(); // huro eat!

这种设计模式在登录框或是注册框,只要是单一使用的场景,可以应用。

class LoginForm {
constructor() {
this.display = "none";
}
show() {
if (this.display === "block") {
console.log("already show!");
}
else {
this.display = "block";
}
}
hide() {
if (this.display === "none") {
console.log("already hide!");
}
else {
this.display = "none";
}
}
} LoginForm.getInstance = (() => {
let instance = null;
return () => {
if (instance === null) {
instance = new LoginForm();
}
return instance;
}
})(); const login1 = LoginForm.getInstance();
const login2 = LoginForm.getInstance(); console.log(login1 === login2);
login1.show();
login2.show(); // already show!

3. 观察者模式

类似于发布订阅,实际上就是当被观察者改变的时候通知观察者。

但是观察者模式是,观察者主动去调用被观察者的函数去观察。

发布订阅模式是,观察者(订阅者)去找一个中间商 (Bus) 去订阅。被观察者(发布者)要发布的时候也找那个中间商。只有中间商知道谁发布了谁订阅了,并及时推送信息。

这里借用柳树的一张图片,如果侵权,请联系我,我将立马删除。

具体观察者模式实现如下

// 观察者模式

// 被观察者
class Subject {
constructor() {
this.state = 0;
this.observers = [];
}
change(fn) {
fn();
this.notifyAll();
}
increase(num) {
this.change(() => {
this.state += num;
})
}
multiply(num) {
this.change(() => {
this.state *= num;
})
}
notifyAll() {
this.observers.forEach(observer => {
observer();
})
}
observe(fn) {
this.observers.push(fn);
}
} class Observer {
constructor({
subject,
name,
fn
}) {
subject.observe(fn);
this.name = name;
}
} const subject = new Subject(); const ob1 = new Observer({
name: 'ob1',
subject,
fn: () => console.log("ob1 observe object")
}) const ob2 = new Observer({
name: 'ob2',
subject,
fn: () => console.log("ob2 observe object")
}) subject.increase(2);

4. 发布订阅模式

class Emitter {
constructor() {
this.map = new Map();
}
on(name, fn) {
if (!this.map.has(name)) {
this.map.set(name, []);
}
const origin = this.map.get(name);
this.map.set(name, [...origin, fn]);
}
emit(name) {
const events = this.map.get(name);
if (events === undefined) {
return;
}
events.forEach(fn => {
fn();
})
}
} const emitter = new Emitter(); emitter.on('click', () => {
console.log("huro");
}) emitter.on('click', () => {
console.log("huro");
}) emitter.on('mousemove', () => {
console.log("huro");
}) emitter.emit('click'); // huro huro

感觉有那味道了,好像这种实现有点类似于浏览器的 addEventListener 只不过 emit 是由用户的 click 等事件去触发的。

总结

本文共介绍了四种设计模式,并在源码层面上给与了实现,部分设计模式也给出了相应的例子,下篇文章中,会继续探讨四种设计模式,分别是 代理模式,迭代器模式,装饰器模式以及状态模式,并结合 Promise 实现, 对象的 for of 循环等进行探讨,欢迎读者阅读。

一篇文章图文并茂地带你轻松学完 JavaScript 设计模式(二)

一篇文章图文并茂地带你轻松学完 JavaScript 设计模式(一)的更多相关文章

  1. 一篇文章图文并茂地带你轻松学完 JavaScript 设计模式(二)

    JavaScript 设计模式(二) 本篇文章是 JavaScript 设计模式的第二篇文章,如果没有看过我上篇文章的读者,可以先看完 上篇文章 后再看这篇文章,当然两篇文章并没有过多的依赖性. 5. ...

  2. 一篇文章图文并茂地带你轻松学完 JavaScript 原型和原型链

    JavaScript 原型和原型链 在阅读本文章之前,已经默认你了解了基础的 JavaScript 语法知识,基础的 ES6 语法知识 . 本篇文章旨在为 JavaScript继承 打下基础 原型 在 ...

  3. 一篇文章图文并茂地带你轻松学完 JavaScript 继承

    JavaScript 继承 在阅读本文章之前,已经默认你了解了基础的 JavaScript 语法知识,基础的 ES6 语法知识 . 继承种类 简单的继承种类可以分为 构造函数继承 原型链继承 clas ...

  4. 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)

    JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...

  5. 一篇文章图文并茂地带你轻松学完 JavaScript 闭包

    JavaScript 闭包 为了更好地理解 JavaScript 闭包,笔者将先从 JavaScript 执行上下文以及 JavaScript 作用域开始写起,如果读者对这方面已经了解了,可以直接跳过 ...

  6. 一篇文章图文并茂地带你轻松实践 HTML5 history api

    HTML5 history api 前言 由于笔者在网络上没有找到比较好的关于 history api 的实践案例,有的案例过于杂乱,没有重点,有些案例只是告诉读者 api 是什么,却没告诉怎么用,本 ...

  7. 一篇文章图文并茂地带你轻松学会 HTML5 storage

    html5 storage api localStorage 和 sessionStorage 是 html5 新增的用来存储数据的对象,他们让我们可以以键值对的形式存储信息. 为什么要有 stora ...

  8. 一篇文章让你快速入门 学懂Shell脚本

    Shell脚本,就是利用Shell的命令解释的功能,对一个纯文本的文件进行解析,然后执行这些功能,也可以说Shell脚本就是一系列命令的集合. Shell可以直接使用在win/Unix/Linux上面 ...

  9. 学完JavaScript基础有感

    紧接上一篇回来了,这几天一直学js,会不自觉的和其他的编程语言联系在一起,在没有学jQuery之前,结合我所学的c,java,数据结构,数据库以及部分html感觉到JavaScript里面又很多相似的 ...

随机推荐

  1. 2021年正确的Android逆向开发学习之路

    2021年正确的Android逆向开发学习之路 说明 文章首发于HURUWO的博客小站,本平台做同步备份发布.如有浏览或访问异常或者相关疑问可前往原博客下评论浏览. 原文链接 2021年正确的Andr ...

  2. SDUST数据结构 - chap3 栈和队列

    一.判断题: 二.选择题: 三.编程题: 7-1 一元多项式求导: 输入样例: 3 4 -5 2 6 1 -2 0 输出样例: 12 3 -10 1 6 0 代码: #include<bits/ ...

  3. [Usaco2008 Mar]牛跑步

    题目描述 BESSIE准备用从牛棚跑到池塘的方法来锻炼. 但是因为她懒,她只准备沿着下坡的路跑到池塘, 然后走回牛棚. BESSIE也不想跑得太远,所以她想走最短的路经. 农场上一共有M (1 < ...

  4. 2.4V升5V芯片,8uA功耗,低功耗升压电路图

    2.4V升5V,可用于USB拔插充电,也可以用于把两节镍氢电池2.4V升压到5V,的固定输出稳压电压值,同时输出电流可达1A,0.5A等 首先是先说下0.5A的这款的话,是比较低功耗的,8uA左右的输 ...

  5. Python-Flask搭建Web项目

    最近因项目需要,学习了用flask搭建web项目,以下是自己的使用感悟 Flask框架结构 static:存储一些静态资源 templates:存储对应的view app.py:涉及到页面的跳转,以及 ...

  6. python_mmdt:一种基于敏感哈希生成特征向量的python库(一)

    概述 python_mmdt是一种基于敏感哈希的特征向量生成工具.核心算法使用C实现,提高程序执行效率.同时使用python进行封装,方便研究人员使用. 本篇幅主要介绍涉及的相关基本内容与使用,相关内 ...

  7. 使用Azure Runbook 发送消息到Azure Storage Queue

    客户需要定时发送信息到Azure Storage Queue,所以尝试使用Azure Runbook实现这个需求. 首先新增一个Azure Automation Account的资源. 因为要使用Az ...

  8. ldf和mdf文件怎么还原到sqlserver数据库

    1.把mdf文件和ldf文件拷贝到数据库的默认路径C:\Program Files\Microsoft SQL Server\MSSQL10.MSSQLSERVER\MSSQL\DATA里:2.在sq ...

  9. LR 场景中Windows资源配置设置

    监视连接前的准备工作 1)进入被监视windows系统,开启以下几个服务Remote Procedure Call(RPC) .Rmote Resgistry.Network DDE.Server.W ...

  10. Most basic operations in Go are not synchronized. In other words, they are not concurrency-safe.

    Most basic operations in Go are not synchronized. In other words, they are not concurrency-safe. htt ...