Честные приватные свойства в прототипе
Привет! За последние 10 лет (С днем рождения, prototype.js!) было написано очень много библиотек для эмуляции полноценного ООП в javascript.Все они, так или иначе, решали задачу реализации приватных членов класса.
Копьев сломано много и в итоге разработчики разделились на 2 части: Первая прячет приватные свойства в scope конструктора и отказывается от использования прототипов (создает методы для каждого экземпляра объекта заново), вторая просто использует соглашение в именах вроде »_privateProperty» и по сути никак не инкапсулирует данные.
Теория:
Ключевое слово new позволяет вызвать функцию таким образом что внутри нее this будет равен пустому объекту с методами прототипа. Таким образом, внутри конструктора можно сформировать объект который вернется из конструктора без явного указания return.
var Animal = function (name){ this._privateName = name; };
Animal.prototype.getName = function (){ return this._privateName; };
var a = new Animal ('Cow'); a._privateName === a.getName (); /* true */ Но если функция вызванная с new явно возвращает любое значение отличное от примитивных типов (строки, числа, NaN и.т.д) то именно этот результат и вернется при том что внутри конструктора через this будет доступен все тот же пустой объект и методы из прототипа.
Практика:
Если принять то что все свойства this приватные, а публичные мы возвращаем явно, то получается изящная эмуляция приватных свойств:
var Animal = function (name){ var self = this;
this._privateName = name;
return { hello: Animal.prototype.hello.bind (this) }; };
Animal.prototype.getName = function (){ return this._privateName; };
Animal.prototype.hello = function (){ return 'hello ' + this.getName (); };
var a = new Animal ('Cow'); a._privateName; /* undefined */ a.getName (); /* Exception */ a.hello (); /* hello Cow */ Для примера я написал простую функцию которая «оборачивает» конструктор и прячет приватные методы из прототипа: github.com/poluyanov/privatize/blob/master/privatize.js
Плюсы:
Основной плюс подобного подхода в том что на большом количестве объектов работа с прототипами и их методами изначально быстрее чем традиционное создание методов на каждый экземпляр объекта: jsperf.com/scopevsprototype Иногда может быть удобно динамически оверрайдить методы прототипа для ряда объектов. Внутри прототипа можно скрыть (По настоящему!) общие поля для множества объектов (Например счетчики). Минусы:
Поскольку функция конструктор возвращает простой объект, не работает instanceof; Такой подход для некоторых может казаться неявным и неочевидным. Способ не претендует на новаторство и возможно вы в своей работе часто пользуетесь таким подходом. Буду рад вашим комментариям.