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. Java并发包源码学习系列:详解Condition条件队列、signal和await

    目录 Condition接口 AQS条件变量的支持之ConditionObject内部类 回顾AQS中的Node void await() 添加到条件队列 Node addConditionWaite ...

  2. 微信小程序API交互的自定义封装

    目录 1,起因 2,优化成果 3,实现思路 4,完整代码 1,起因 哪天,正在蚂蚁森林疯狂偷能量的我被boss叫过去,告知我司要做一个线上直播公开课功能的微信小程序,博主第一次写小程序,复习了下文档, ...

  3. Hbase Region合并

    业务场景: Kafka+SparkStreaming+Hbase由于数据大量的迁移,再加上业务的改动,新增了很多表,导致rerigon总数接近4万(36个节点) 组件版本: Kafka:2.1.1 S ...

  4. 【Linux】ssh远程连接到指定ip的指定用户上

    通过ssh可以远程连接到其他的机器上,但是如果只想连接到指定的用户的话 需要这样做: -l 选项 (是L不是I,小写) ssh IP -l 用户名 这里的ip如果在hosts下就可以直接输入域名或者主 ...

  5. CTFshow-萌新赛逆向_flag白给

    查看题目信息 下载后得到一个flag.exe文件,进行测试 使用PEiD查壳 发现一个upx的壳 使用命令进行解壳 upx -d 拿到一个无壳的程序 放进OD打开,查找关键词 发现信息 成功拿到序列号 ...

  6. LOOP语句的AT语句块

    在loop一个内表的时候,如果想在loop循环中使用AT NEW ,AT END OF 等语句,一定需要注意的几点: 1.内表要排序 2.AT END OF 语句中影响的是指定字段前面所有的字段 3. ...

  7. 24V转5V芯片,高输入电压LDO线性稳压器

    PW6206系列是一个高精度,高输入电压低静态电流,高速,低功耗降线性稳压器具有高纹波抑制.输入电压高达40V,负载电流为在VOUT=5V和VIN=7V时高达300mA.该设备采用BCD工艺制造.PW ...

  8. MYSQL(将数据加载到表中)

    1. 创建和选择数据库 mysql> CREATE DATABASE menagerie; mysql> USE menagerie Database changed 2. 创建表 mys ...

  9. 中断与系统调用深度分析(以网络编程接口SocketAPI为例)

    1.从计算机CPU与I/O设备的交互方式谈起 计算机CPU与I/O设备的交互方式有最早的程序查询(也叫轮询)方式,发展到后来的程序中断方式,DMA方式等.简单来说,最早的程序查询方式的机制是,CPU若 ...

  10. 欢迎来到 ZooKeeper 动物世界

    本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费有趣.入门级的 ZooKeeper 开源教程,面向有编程基础的新手. Zo ...