Получив огромное вдохновение от работы Кристофа, я придумал слегка модифицированную концепцию, которая предоставляет несколько улучшений. Опять же, это решение интересно, но не обязательно рекомендуется. Эти улучшения включают в себя:
- Больше не нужно выполнять какие-либо настройки в конструкторе
- Убрана необходимость хранить публичный GUID в экземплярах
- Добавлен синтаксический сахар
По сути, хитрость заключается в том, чтобы использовать сам объект экземпляра в качестве ключа для доступа к связанному частному объекту. Обычно это невозможно для простых объектов, поскольку их ключи должны быть строками. Однако я смог сделать это, используя тот факт, что выражение ({} === {})
возвращает false
. Другими словами, оператор сравнения может различать уникальные экземпляры объектов.
Короче говоря, мы можем использовать два массива для обслуживания экземпляров и связанных с ними частных объектов:
Foo = (function() {
var instances = [], privates = [];
// private object accessor function
function _(instance) {
var index = instances.indexOf(instance), privateObj;
if(index == -1) {
// Lazily associate instance with a new private object
instances.push(instance);
privates.push(privateObj = {});
}
else {
// A privateObject has already been created, so grab that
privateObj = privates[index];
}
return privateObj;
}
function Foo() {
_(this).bar = "This is a private bar!";
}
Foo.prototype.getBar = function() {
return _(this).bar;
};
return Foo;
})();
Вы заметите функцию _
выше. Это функция доступа для захвата частного объекта. Он работает лениво, поэтому, если вы вызовете его с новым экземпляром, он создаст новый приватный объект на лету.
Если вы не хотите дублировать код _
для каждого класса, вы можете решить эту проблему, поместив его в фабричную функцию:
function createPrivateStore() {
var instances = [], privates = [];
return function (instance) {
// Same implementation as example above ...
};
}
Теперь вы можете уменьшить его до одной строки для каждого класса:
var _ = createPrivateStore();
Опять же, вы должны быть очень осторожны , используя это решение, поскольку оно может создавать утечки памяти, если вы не реализуете и вызываете функцию уничтожения при необходимости.