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

в 16:31, , рубрики: javascript, Веб-разработка, ооп, ооп js

Привет!

За последние 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. Такой подход для некоторых может казаться неявным и неочевидным.

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

Автор: poluyanov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js