1.前言
- 使用场景:
- 当创建一个对象的前置操作会耗费非常多的资源时。比如初始化一个实例需要查询多个数据库的时候,这个时候我们只需要考虑输入输出时候存在唯一对应关系,如果是的话就可以将结果缓存起来,当下次需要再次生产该类型的对象时clone一份就可以,节省了巨大的系统资源。
- 当类的实例种类有限时。因为原型模式实际上就是一种缓存模式,当类的实例类型很多的时候比如:animals类,根据场景不同产生的实例有接近无限多个,这个时候使用原型模式(缓存)肯定是得不偿失的。
- 优点:
- 节省资源。
- 逃避构造函数的约束。
- 缺点
- 由于是clone方式,所以引用类型引起的隐患使我们必须考虑的,根据不同场景使用不同的方式实现原型模式。
- 系统复杂度必然有所提高。对于复杂场景的适配可能会造成复杂度的成倍提升(和new一个对象相比,判断是浅拷贝还是深拷贝,什么类型用什么设计,显然复杂多了,建议对简单对象和必须优化对象使用该模式)。
2. 例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>原型模式</title>
</head>
<body>
<script>
class Utils {
constructor() {
this.map = new WeakMap();
this.deepCopy = this.deepCopy.bind(this);
}
deepCopy(obj){
let { map } = this;
let deepCopy = this.deepCopy;
//如果是基本类型则直接返回
if (typeof obj !== "object") {
return obj;
}
//如果是引用类型,则判断是否是循环引用的对象
if (map.get(obj)) {
return {};
}
//如果是null
if (!obj) {
return null;
}
//此时细分类型
const obj_type = Object.prototype.toString.call(obj);
let temp_obj = null;
//如果是数组则迭代deepcopy
if (obj_type.includes("Array")) {
//缓存obj,留待下次判断是否循环引用
map.set(obj, obj_type);
temp_obj = [];
obj.forEach((item) => {
temp_obj.push(deepCopy(item));
});
}
//如果是对象的话,迭代deepcopy直到基本类型
if (obj_type.includes("Object")) {
map.set(obj, obj_type);
temp_obj = {};
Reflect.ownKeys(obj).forEach((key) => {
temp_obj[key] = deepCopy(obj[key]);
});
}
return temp_obj;
}
}
let u = new Utils();
class DbItem {
constructor(name, createTime) {
this.dbName = name;
this.createTime = createTime;
}
}
class GetDbItem{
constructor(){
this.dbItemMap = new Map();
}
getDbItem(name){
let {dbItemMap} = this;
if(!name){
throw new Error("no name!")
return ;
}
//clone原型
if(dbItemMap && dbItemMap.get(name)){
console.log("cloned");
return u.deepCopy(dbItemMap.get(name));
}
//生成原型
let newDb = new DbItem(name,Date.now());
dbItemMap.set(name,newDb);
return newDb;
}
}
let getDBItemInstance = new GetDbItem();
let d1 = getDBItemInstance.getDbItem("db1");
let d2 = getDBItemInstance.getDbItem("db1");
console.log(d1);
console.log(d2);
</script>
</body>
</html>
3. 后记
原型其实就是缓存功能+clone功能,使用时记得关注内存占用和使用场景,否则可能会造成不必要的浪费。