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. 基于Docker搭建Hadoop+Hive

    为配合生产hadoop使用,在本地搭建测试环境,使用docker环境实现(主要是省事~),拉取阿里云已有hadoop镜像基础上,安装hive组件,参考下面两个专栏文章: 克里斯:基于 Docker 构 ...

  2. 改进你的c#代码的5个技巧(四)

    像每一篇文章一样,我会重复几行.我在我的Core i3 CPU.4GB主内存和Windows 7平台上测试了以下代码.如果你在不同的硬件配置或使用不同的平台,那么你的输出可能会随着我的输出屏幕而变化, ...

  3. Java程序入门

    编写Java源程序 在d:\day01 目录下新建文本文件,完整的文件名修改为HelloWorld.java ,其中文件名为HelloWorld ,后缀名必须为.java . 用记事本打开 在文件中键 ...

  4. CISCO 如何重置3850交换机密码

    SUMMARY STEPS: Connect a terminal or PC to the switch. Set the line speed on the emulation software ...

  5. Zerotier在windows下实现内网远程桌面

    Zerotier实现内网远程桌面 使用背景 实验室设备条件过于恶劣 向日葵在有些场景下会莫名崩溃,或者画面不动. Teamviewer免费版在之前用的时候出现过疑似商业行为被断连,github上寻解决 ...

  6. WPF Selector、SelectIndex、SelectedValue、SelectedValuePath、SelectedItem这几兄弟你分的清楚嘛?

    Selector Selector是一个抽象类,继承ItemsControl类(包含任何类型的对象(例如字符串,图像或面板)的集合),而本文的4个兄弟都是Selector类下的4个属性. Select ...

  7. 使用JWT创建安全的ASP.NET Core Web API

    在本文中,你将学习如何在ASP.NET Core Web API中使用JWT身份验证.我将在编写代码时逐步简化.我们将构建两个终结点,一个用于客户登录,另一个用于获取客户订单.这些api将连接到在本地 ...

  8. 洛谷 P4999

    题目链接: P4999 烦人的数学作业 题目大意 详见题目 solution 有一个显而易见的结论 发现 \(ans_{l, r} = ans_{1. r} - ans_{1, l - 1}\) 那只 ...

  9. LOJ2130软件包

    题目描述Linux用户和OSX用户一定对软件包管理器不会陌生.通过软件包管理器,你可以通过一行命令安装某一个软件包,然后软件包管理器会帮助你从软件源下载软件包,同时自动解决所有的依赖(即下载安装这个软 ...

  10. TRUNK与VTP

    TRUNK协议: 交换机之间VLAN通信: 同一个VLAN可以跨越多个交换机 主干功能支持多个VLAN的数据 Trunk(主干) VLAN 中交换机之间的链路:用来承载多个VLAN的数据流. Trun ...