知识点
- Proxy
- 单例模式
题目
单例模式(Singleton)是一种常用的软件设计模式,它保证我们系统中的某一个类在任何情况实例化的时候都获得同一个实例。例如:
const root1 = new Root()
const root2 = new Root()
const root3 = new Root()
root1 === root2 // true
root2 === root3 // true
我们构造一个名为singletonify方法,可以传入一个用户自定义的类,可以返回一个新的单例模式的类。例如:
class A () {}
const SingleA = singletonify(A)
const a1 = new SingleA()
const a2 = new SingleA()
const a3 = new SingleA()
a1 === a2 // => true
a2 === a3 // => true
注意,你要保证singletonify返回的类的实例也是原来的类的实例:
a1 instanceof A // => true
a1 instanceof SingleA // => true
自定义的类属性也要保持一致,例如:
class A () {}
A.staticMethod = () => {}
const SingleA = singletonify(A)
SingleA.staticMethod === A.staticMethod // => true
实现
分析并拆解题目
(1)首先要求singletonify 能够通过new生成实例,所以singletonify需要是一个构造函数
(2)singletonify生成的实例是一个单例,也就是多次调用,返回同一个对象,所以需要通过闭包实现一个保存在内存中的对象作为调用返回的单例
(3)要求返回的实例是原来类的实例,并且singletonify继承了原来的类的静态方法,所以需要使用extends来实现继承
extends来实现继承的原理是:
class A extends from B {
constructor () {
super ()
}
}
// 等同于
Object.setPrototypeOf(A, B);
Object.setPrototypeOf(A.prototype, B.prototype);
Object.setPrototypeOf(A, B)的作用是A.__proto__ = B,实现了A对B的静态方法的继承
Object.setPrototypeOf(A.prototype, B.prototype)作用是A.prototype.__proto__ = B.prorotype,如果假设a是A的实例,那么就实现了下面的继承链:
a.__proto__ === A
A.prototype.__proto__ === B.prorotype
(4)extends的constructor的返回值和构造函数返回值在是Object类型时是相同的(constructor不能返回基本类型,构造函数可以但是会忽略),默认情况是返回this,我们这里要返回的就是那个单例
所以有:
const singletonify = (OriginalClass) => {
let single = new OriginalClass();
class SingleClass extends OriginalClass {
constructor (...props) {
super(...props);
return single
}
}
return SingleClass
};
(5)到这里我们所有要求都满足了,除了下面这一条:
a1 instanceof SingleA
命名是通过new SingleA产生的实例,反而不是SingleA的实例,这就是因为我们在constuctor里面没有返回this,而是返回的A的实例
所以要对返回的结果的原型链在进行修正
(笨死了,看了别人的答案才明白的,为什么自己想不到,蠢货一个)
a1 instanceof SingleA的关系实现就是因为下面的继承链存在:
a1.__proto__ === SingleA.prototype
而我们现在的继承链是:
a1.__proto__ === A.prototype
所以需要:
Object.setPrototype(a1, SingelA.prototype)
所以目前完整的原型链是:
a1.__proto__ === SingelA.prototype;
// a1是SingelA的实例
a1.__proto__.__proto__ === SingelA.prototype.__proto__ === A.prototype;
// SingelA继承了A, 所以a1是A的实例
SingleA.__proto__ === A;
// SingleA继承了A的静态方法
最终的结果是:
const singletonify = (OriginalClass) => {
let single = new OriginalClass();
class SingleClass extends OriginalClass {
constructor (...props) {
super(...props);
return single
}
}
Object.setPrototypeOf(single, SingleClass.prototype);
return SingleClass
};
Proxy
翻看了评论区,发现还可以用Proxy实现
(Proxy真是太强大了,什么操作都可以拦截么)
Proxy(target, handler)中的constructor可以拦截new操作符
var p = new Proxy(target, {
construct: function(target, argumentsList, newTarget) {
}
});
target是目标对象。argumentsList是constructor的参数列表。newTarget是最初被调用的构造函数,就上面的例子而言是p。
construct中的this会绑定到handler上
所以可以这样实现:
const singletonify = (OriginalClass) => {
let single = new OriginalClass();
return new Proxy(OriginalClass, {
construct(target, arguments, newTarget) {
return single
}
});
};
因为返回的Proxy是对原来的构造函数的拦截,所以会继承原来的构造函数的特性,而因为construct中的this会绑定到handler上,所以返回的实例也是singletonify的实例
本文深入探讨单例模式的概念及其实现,包括通过构造函数和闭包创建单例,以及如何利用Proxy拦截new操作符实现单例。同时,文章详细解析了继承和原型链在实现过程中的关键作用。
361

被折叠的 条评论
为什么被折叠?



