这是我参与「第五届青训营 」伴学笔记创作活动的第12天。
一、本堂课重点内容
- 原型模式
- 代理模式
- 迭代器模式
二、详细知识点介绍
原型模式
复制已有对象来创建新的对象,是JS中对象创建的基本模式。
在某些特定场景下,较大的对象初始化时,通过复制一个已有的对象来创建新的对象会有更好的性能和更小的内存使用。
通过调用Object.create(baseObj)实现,基于一个已有对象,返回一个新的对象,新对象与baseObj是继承关系,会继承baseObj的所有属性和方法。
但其他语言中几乎不会用到原型模式创建对象。
代理模式(JS中)
可自定义控制对原对象的读写方式,并且允许在更新前后做一些额外处理,类似hook之类的额外处理。
如监控,监控浏览器发出的所有请求的成功率,进行记录,可以封装fetch,每次发送时做一个统计,或者改写原本的fetch,代理了fetch;代理工具、前端框架中对网络请求进行劫持等。
Proxy
JavaScript中的Proxy,表示“代理”某些操作,可以译为“代理器”。
let proxy = new Proxy(target, handle);
Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。 其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
迭代器模式
不暴露数据类型的情况下访问集合中的数据,如数组是value的集合,map是k-v的集合,set是key的集合等。
这些数据结构中提供了通用操作接口,JavaScript中可以给任意对象添加内置方法[Symbol.iterator]让一个对象变成可迭代的,该方法返回一个对象,返回的对象中有一个叫做next的属性,就是每次执行for-of的时候会被调用的函数。next函数返回两个值,value和done,value是下一个值,done是迭代是否完成。
JavaScript提供以下写法:
const numbers = [1, 2, 3];
const map = new Map();
map.set("k1", "v1");
map.set("k2", "v2");
const set = new Set(["a1", "a2", "a3"]);
for (const number of numbers) {
}
for (const [k, v] of map) {
}
for (const key of set) {
}
代理模式(前端框架中)
前端框架中对DOM操作的代理
框架中对DOM的操作实际上都是更新的虚拟DOM,前端框架对用户操作进行代理,然后框架再进行真正DOM的更新,框架还提供了一些hooks,如
onBeforeUpdate()和onUpDated()等。
组合模式
可多个对象组合使用,成为单个对象;也可以单个对象独立使用。
DOM结构,前端组件,文件目录等,如React中
const App=()=>{
return(
<div className="App">
<Header />
<Count />
</div>
)
}
三、实践实习例子
原型模式
对于上例中的User,创建一个baseUser对象作为模板,通过Object.create(baseUser);创建新对象并初始化。
const baseUser = new User("");
export const createUser = (name: string) => {
const user: User = Object.create(baseUser);
user.name = name;
user.followers = [];
return user;
};
Proxy的用法示例
var p = new Proxy(obj, {
//get 属性读取时的操作
get(target, prop) {
//target 就是 obj 这个对象
//prop 是 obj 的属性名
if (prop in target) {
//如果该属性名存在与该对象中,则返回该属性值
return target[prop];
} else {
//否则返回字符串 "该对象没有该属性"
return "该对象没有该属性";
}
},
//set 属性设置时的操作
set(target, prop, value) {
//target 就是 obj 这个对象
//prop 是 obj 的属性名
//value 是 要设置的值
if (prop === "age") {
//如果该属性名是age,则返回修改后的值
target[prop] = value;
} else {
//否则弹出异常内容
throw "除年龄外,其它属性不可以更改";
}
}
})
对象迭代器示例
构造了一个自定义数组
class MyArray {
arr = [];
push(num) {
if (Number.isInteger(num)) {
this.arr.push(num);
}
}
pop() {
return this.arr.pop();
}
[Symbol.iterator]() {
let i = 0;
return {
next: () => {
if (i < this.arr.length) {
return { value: this.arr[i++], done: false };
} else {
return { value: null, done: true };
}
}
};
}
}
const arr = new MyArray();
for (let i = 0; i < 10; ++i) {
arr.push(i);
}
for (const num of arr) {
console.log(num);
}
四、课后个人总结
本节课介绍了原型模式、代理模式和迭代器模式,主要是是JavaScript一些独特的设计模式,并结合Vue和React的实际例子介绍了前端框架中的代理模式和组合模式,并分析了传统设计模式和现代编程中的一些新思想。
传统的设计模式已经不太适用于现代的程序设计,随着技术的发展,编程语言带来的新特性可能会很容易的解决传统编程带来的问题。
设计模式不是万能的:
- 总结出的抽象的模式相对比较简单,但是想要将抽象的模式套用到场景中非常困难,需要看个人开发经验,如很难界定一个对象或一个类只干一件事情;
- 现代编程语言的多编程范式带来更多可能性,通过JavaScript的一些特性可能很容易的解决传统编程问题;
- 需要不断学习优秀开源项目的设计模式。
五、参考链接
js中的Proxy_拾玥花开的博客-CSDN博客_js proxy
JS Proxy (代理)_粥猫ZHoumao的博客-CSDN博客_js proxy
前端设计模式应用.pptx - 飞书云文档 (feishu.cn)