大家都知道,基于原型链的JavaScript是不提供“私有属性”与“私有方法”的,你要用私有的话,可以,在构造函数里一口气把私有的都写好,然后公开一些接口作为共有属性与方法。
但如果你这么做,你就不得不舍弃原型链,至少上述公开出来的调用了私有属性与方法的接口是不能写在prototype上的,否则就不是私有了,大家都能用。
至于说为什么不在prototype上写是一个问题,这主要是如果你这个类有多个实例化的话,那么就会有多个方法与属性的实例,浪费开销。
这个情况当然不是一成不变的,在ES6以前,要绕过去,当然是有方法的,比如说做两个Array,一一映射地保存当前类实例与其私有属性及方法,这样写在这个类的prototype上的方法就可以通过查询this来获得私有方法与属性几。
这个方法多少有些绕,而且存在各种问题,其中最头疼的恐怕就是去重(每次添加的时候都要搜索一下)与内存溢出。
这是在ES6下会容易解决,因为你现在可以直接使用Map/WeakMap。
下面就给一个例子,利用WeakMap和Set来做一个事件管理器:
(function (root) {
var mEventPool = new WeakMap();
class EventManager {
constructor () {
var pool = {};
mEventPool.set(this, pool);
}
emit (event, ...args) {
if (event === 'emit' || event === 'hook') return;
var pool = mEventPool.get(this);
if (!pool) return;
pool = pool[event];
if (!pool) return;
pool.forEach((callback) => callback.apply(lifeController, args));
}
hook (event, callback) {
if (event === 'emit' || event === 'hook') return;
var pool = mEventPool.get(this);
if (!pool) return;
if (!pool[event]) {
pool[event] = new Set();
}
pool = pool[event];
if (pool.has(callback)) return;
pool.add(callback);
}
}
root.EventManager = EventManager;
}) (window);
每个实例本身都在mEventPool里注册一个个人的“私有对象”,公开方法可以通过this从mEventPool获取这个私有对象,从而完成公开访问私有。
WeakMap只允许非基础类型的对象作为key,在这里刚好够用。
Set不允许有重复,这是比Array好的地方,用来保存callback(当然,这自然也就要求了同一个callback不能注册多次)。
至于说为什么这里每个EM的pool使用传统的object而不用Map(WeakMap不允许基础类型作为key,所以这里肯定不能用),则是因为在这个情况完全没必要用Map。。。这毕竟也是开销啊。。。
至于说还能怎么玩,这个就大家自己去开脑洞吧。
反正,私有以后,似乎也不是特别有用。。。每一次读写都要从WeakMap里索取对象,这也是开销,这点远不如传统方法来得好——这当然是废话了。