JavaScript开发者应懂的33个概念26-设计模式
目录
- 调用堆栈
- 原始类型
- 值类型和引用类型
- 隐式, 显式, 名义和鸭子类型
- == 与 ===, typeof 与 instanceof
- this, call, apply 和 bind
- 函数作用域, 块级作用域和词法作用域
- 闭包
- map, reduce, filter 等高阶函数
- 表达式和语句
- 变量提升
- Promise async 与 wait
- 立即执行函数, 模块化, 命名空间
- 递归
- 算法
- 数据结构
- 消息队列和事件循环
- setTimeout, setInterval 和 requestAnimationFrame
- 继承, 多态和代码复用
- 按位操作符, 类数组对象和类型化数组
- DOM 树和渲染过程
- new 与构造函数, instanceof 与实例
- 原型继承与原型链
- Object.create 和 Object.assign
- 工厂函数和类
- 设计模式
- Memoization
- 纯函数, 函数副作用和状态变化
- 耗性能操作和时间复杂度
- JavaScript 引擎
- 二进制, 十进制, 十六进制, 科学记数法
- 偏函数, 柯里化, Compose 和 Pipe
- 代码整洁之道
简介
记录一个重新学习javascript的过程 ,文章并不是按顺序写的,写完就会更新目录链接 本篇文章目录是参照 @leonardomso 创立,英文版项目地址在这里
1.工厂模式
工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。
大白话说的意思就是:更方便地去创建实例
比如vue中 axios.create就是工厂模式的运用 axios.create()每次返回的都是一个全新的实例
例子
function createPerson(name, age, gender) {
var person = new Object();
person.name = name;
person.age = age;
person.gender = gender;
person.sayName = function () {
console.log(this.name);
}
return person;
}
//利用工厂函数来创建对象
var person1 = createPerson("zhangsan", 18, 'male');
var person2 = createPerson("lisi", 20, 'female');
createPerson
就像一个工厂 我们只需传入不通的参数便可以创建出来不通的新的对象
2.单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,这样的模式就叫做单例模式。
使用场景 Vuex: Vuex实现了一个全局的 Store 用于存储应用的所有状态。这个 Store 的实现,正是单例模式的典型应用,用Vue.use()
方法,我们安装了 Vuex 插件。Vuex 插件是一个对象,它在内部实现了一个 install 方法,这个方法会在插件安装时被调用,从而把 Store 注入到Vue实例里去。也就是说每 install 一次,都会尝试给 Vue 实例注入一个 Store
使用场景 还有弹出全局模态框
例子
// 单例模式
var Singleton = function(name){
this.name = name;
};
Singleton.prototype.getName = function(){
return this.name;
}
// 获取实例对象
var getInstance = (function() {
var instance = null;//闭包中改变的变量相当于一个全局变量
return function(name) {
console.log("111",instance);
console.log("222",name)
if(!instance) {
instance = new Singleton(name);
}
return instance;
}
})();
// 测试单体模式的实例
var a = getInstance("aa");
var b = getInstance("bb");
var c = getInstance("cc");
console.log(a === b); // true
console.log(a.getName());// aa
console.log(b.getName());// aa
console.log(c.getName());//aa
单例模式大白话的意思就是: 一个类只能构造出唯一实例 然后在全局使用
3.策略模式
根据不同参数可以命中不同的策略
优点
- 能减少大量的 if 语句
- 复用性好
年终奖的 demo, 根据不同的参数(level)获得不同策略方法(规则), 这是策略模式在 JS 比较经典的运用之一
const S = function(salary) {
return salary * 4
}
const A = function(salary) {
return salary * 3
}
const B = function(salary) {
return salary * 2
}
const calculateBonus = function(func, salary) {
return func(salary)
}
calculateBonus(A, 10000) // 30000
calculateBonus(S, 20000) // 80000
calculateBonus(B, 30000) // 60000
4.适配器模式
适配器模式通俗点说就是:将一种格式适配成你所需要的格式
适配器模式可用来在现有接口和不兼容的类之间进行适配。它被添加到现有代码中来协调两个不同的接口。就类似于苹果电脑的 type-c 接口和U盘之间的接口转换器。
我们现在客户系统有一个对象 clientObj,而我们有一个现有的方法 interfaceMethod:
var clientObj = {
name: 'Jack',
phone: '13333333333',
address: 'China'
}
function interfaceMethod(str1, str2, str3){
...
}
这时要把 clientObj 作为参数传给这个方法,需要一个适配器:
function clientToInterfaceAdapter(o) {
interfaceMethod(o.name, o.phone, o.address)
}
// 利用适配器传递对象
clientToInterfaceAdapter(clientObj)
适配器模式在 vue 中的应用
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
message 这个属性的值是 ‘Hello’,但我想要的是一个倒转的 ‘Hello’, 通过computed 中的 reversedMessage 方法, 我们得到了目标 ‘olleh’,且没有改变原有的数值。这里的 reversedMessage 就相当于一个适配器。
适配器模式在 ts 中的应用
type Person = {
name?: string,
age?: number
}
5.装饰器模式
原有方法维持不变,在原有方法上再挂载其他方法来满足现有需求,装饰器模式通俗点说就是:定义一个类,在不改这个类的前提下,给这个类拓展功能
例子
class Circle{
draw() {
console.log('画一个圆形')
}
}
class Decorator {
constructor(circle) {
this.circle = circle
}
draw() {
this.circle.draw()
this.setRedBorder(circle)
}
setRedBorder(circle) {
console.log('设置红色边框')
}
}
// 测试
let circle = new Circle()
circle.draw()
let dec = new Decorator(circle)
dec.draw()
6.代理模式
在JavaScript中,代理模式是一种常见的设计模式,它允许我们在不改变对象本身的情况下,通过代理对象来控制对象的访问
代理模式通俗易懂点说就是:为对象提供一种代理,便以控制对这个对象的访问,不能直接访问目标对象
最好的实践场景就是ES6 Proxy
let target = {
name: 'Tom',
age: 18
};
let proxy = new Proxy(target, {
get(target, key) {
console.log(`get ${key}`);
return target[key];
},
set(target, key, value) {
console.log(`set ${key}=${value}`);
target[key] = value;
}
});
proxy.name; // 输出:get name
proxy.age = 20; // 输出:set age=20
7.观察者模式
当观察对象发生变化时自动调用相关函数,观察者模式通俗点讲就是:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
生活中场景的例子就是
某人对某个事情敢兴趣,不是自己一直盯着那个事情,而是发生的事件对象 去通知对这个事情敢兴趣的人
例子
//观察者设计模式
//发布者 -->商家
var shopObj = {};
//商品列表 [key:[]], key为商品名
shopObj.list = [];
//订阅方法
shopObj.listen = function ( key, fn) {// key是商品型号, fn这个函数就是订阅的行为
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);//往商品名为key的商品列表中添加订阅
}
//发布消息方法
shopObj.publish = function (key) {
//var key = arguments[0];//如果不传参数key,这样也可以
var fns = this.list[key];
// for (var i = 0; i < fns.length; i++) {
for(var i = 0 ,fn; fn = fns[i++];){
//执行订阅的函数fn arguemnts储存的所有实参
// var fn = fns[i++];
fn.apply(this, arguments)
}
}
//A用户添加订阅
shopObj.listen("华为", function (brand, model) {
console.log( "A用户收到:" + brand + model + "手机降价了");
})
//B用户添加订阅
shopObj.listen("华为", function (brand, model) {
console.log("B用户收到:" + brand + model + "手机降价了");
})
//c用户添加订阅
shopObj.listen("小米", function (brand, model) {
console.log("C用户收到:" + brand + model + "手机降价了");
})
//双11 商家发布消息华为降价的信息
shopObj.publish("华为", "p30");
shopObj.publish("小米", "Mix4");
8.发布订阅模式
我们使用的vue框架就是发布订阅模式,先借用官网的双向绑定原理图:
下面稍微解释一下这个图(框架源码整个过程比较复杂,如果现在看不懂下面几段也没关系,大致了解一下即可)。
组件渲染函数(Component Render Function)被执行前,会对数据层的数据进行响应式化。响应式化大致就是使用
Object.defineProperty
把数据转为getter/setter
,并为每个数据添加一个订阅者列表的过程。这个列表是getter
闭包中的属性,将会记录所有依赖这个数据的组件。
- 也就是说,响应式化后的数据相当于发布者。
- 每个组件都对应一个
Watcher
订阅者。当每个组件的渲染函数被执行时,都会将本组件的Watcher
放到自己所依赖的响应式数据的订阅者列表里,这就相当于完成了订阅,一般这个过程被称为依赖收集(Dependency Collect
)。 - 组件渲染函数执行的结果是生成虚拟
DOM
树(Virtual DOM Tree
),这个树生成后将被映射为浏览器上的真实的 DOM 树,也就是用户所看到的页面视图。 - 当响应式数据发生变化的时候,也就是触发了
setter
时,setter
会负责通知(Notify
)该数据的订阅者列表里的Watcher
,Watcher
会触发组件重渲染(Trigger re-render
)来更新(update
)视图。
这就是发布订阅模式 有兴趣的同学可以去研究一下源码