一篇文章图文并茂地带你轻松学完 JavaScript 设计模式(一)
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 设计模式(一)的更多相关文章
- 一篇文章图文并茂地带你轻松学完 JavaScript 设计模式(二)
JavaScript 设计模式(二) 本篇文章是 JavaScript 设计模式的第二篇文章,如果没有看过我上篇文章的读者,可以先看完 上篇文章 后再看这篇文章,当然两篇文章并没有过多的依赖性. 5. ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 原型和原型链
JavaScript 原型和原型链 在阅读本文章之前,已经默认你了解了基础的 JavaScript 语法知识,基础的 ES6 语法知识 . 本篇文章旨在为 JavaScript继承 打下基础 原型 在 ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 继承
JavaScript 继承 在阅读本文章之前,已经默认你了解了基础的 JavaScript 语法知识,基础的 ES6 语法知识 . 继承种类 简单的继承种类可以分为 构造函数继承 原型链继承 clas ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 事件循环机制(event loop)
JavaScript 事件循环机制 (event loop) 本篇文章已经默认你有了基础的 ES6 和 javascript语法 知识. 本篇文章比较细致,如果已经对同步异步,单线程等概念比较熟悉的读 ...
- 一篇文章图文并茂地带你轻松学完 JavaScript 闭包
JavaScript 闭包 为了更好地理解 JavaScript 闭包,笔者将先从 JavaScript 执行上下文以及 JavaScript 作用域开始写起,如果读者对这方面已经了解了,可以直接跳过 ...
- 一篇文章图文并茂地带你轻松实践 HTML5 history api
HTML5 history api 前言 由于笔者在网络上没有找到比较好的关于 history api 的实践案例,有的案例过于杂乱,没有重点,有些案例只是告诉读者 api 是什么,却没告诉怎么用,本 ...
- 一篇文章图文并茂地带你轻松学会 HTML5 storage
html5 storage api localStorage 和 sessionStorage 是 html5 新增的用来存储数据的对象,他们让我们可以以键值对的形式存储信息. 为什么要有 stora ...
- 一篇文章让你快速入门 学懂Shell脚本
Shell脚本,就是利用Shell的命令解释的功能,对一个纯文本的文件进行解析,然后执行这些功能,也可以说Shell脚本就是一系列命令的集合. Shell可以直接使用在win/Unix/Linux上面 ...
- 学完JavaScript基础有感
紧接上一篇回来了,这几天一直学js,会不自觉的和其他的编程语言联系在一起,在没有学jQuery之前,结合我所学的c,java,数据结构,数据库以及部分html感觉到JavaScript里面又很多相似的 ...
随机推荐
- CSS 奇思妙想边框动画
今天逛博客网站 -- shoptalkshow,看到这样一个界面,非常有意思: 觉得它的风格很独特,尤其是其中一些边框. 嘿嘿,所以来一篇边框特辑,看看运用 CSS,可以在边框上整些什么花样. bor ...
- 【Spring】Spring的数据库开发 - 1、Spring JDBC的配置和Spring JdbcTemplate的解析
Spring JDBC 文章目录 Spring JDBC Spring JdbcTemplate的解析 Spring JDBC的配置 简单记录-Java EE企业级应用开发教程(Spring+Spri ...
- 【ORA】ORA-00030: User session ID does not exist.
今天巡检,查询锁相关的情况的时候,确认业务后,准备将锁干掉,但是干掉的时候报了一个错误,ORA-00030 发现回话不存在,我以为pmon进程已经将锁进程kill掉了,就再次查看,发现,还是存在 这个 ...
- 【Oracle】10.2.0.1升级到10.2.0.5
升级数据库到10.2.0.5 因是测试环境,不需要备份:如是生产系统,建议进行全备份后再进行升级操作,预防数据丢失造成不必要的影响. 步骤: 上传并解压补丁,安装前准备,安装补丁,预升级检查, ...
- Flutter 自定义列表以及本地图片引用
前言 上篇关于Flutter的文章总结了下标签+导航的项目模式的搭建,具体的有需要的可以去看看Flutter分类的文章,这篇文章我们简单的总结一下关于Flutter本地文件引用以及简单的自定义List ...
- 1.搭建Hadoop实验平台
节点功能规划 操作系统:CentOS7.2(1511) Java JDK版本:jdk-8u65-linux-x64.tar.gz Hadoop版本:hadoop-2.8.3.tar.gz 下载地址: ...
- STGAN: A Unified Selective Transfer Network for Arbitrary Image Attribute Editing 阅读笔记和pytorch代码解读
一.论文采用的新方法 1.AttGan中skip connect的局限性 由于encoder中对特征的下采样实际上可能损失部分特征,我们在decoder中进行上采样和转置卷积也无法恢复所有特征,因此A ...
- OLED的波形曲线、进度条、图片显示(STM32 HAL库 模拟SPI通信 5线OLED屏幕)详细篇
少废话,先上效果图 屏幕显示效果 全家福 一.基础认识及引脚介绍 屏幕参数: 尺寸:0.96英寸 分辨率:128*64 驱动芯片:SSD1306 驱动接口协议:SPI 引脚说明: 二. ...
- 从零搭建一个IdentityServer——集成Asp.net core Identity
前面的文章使用Asp.net core 5.0以及IdentityServer4搭建了一个基础的验证服务器,并实现了基于客户端证书的Oauth2.0授权流程,以及通过access token访问被保护 ...
- 手写Netty之多路复用Select小案例
注意:本文只是将上文多路复用器Select.Poll.Epoll区别梳理中提出的概念与Netty中的步骤联系起来,方便后面回顾,代码中注释很多,对于大家来说如果不是怀有同样的目的,不一定有用. 单线程 ...