Pull to refresh

Честные приватные свойства в прототипе

Reading time 2 min
Views 12K
Привет!

За последние 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

Плюсы:
  1. Основной плюс подобного подхода в том что на большом количестве объектов работа с прототипами и их методами изначально быстрее чем традиционное создание методов на каждый экземпляр объекта: jsperf.com/scopevsprototype
  2. Иногда может быть удобно динамически оверрайдить методы прототипа для ряда объектов.
  3. Внутри прототипа можно скрыть(По настоящему!) общие поля для множества объектов(Например счетчики).


Минусы:
  1. Поскольку функция конструктор возвращает простой объект, не работает instanceof;
  2. Такой подход для некоторых может казаться неявным и неочевидным.


Способ не претендует на новаторство и возможно вы в своей работе часто пользуетесь таким подходом. Буду рад вашим комментариям.
Tags:
Hubs:
+10
Comments 31
Comments Comments 31

Articles