Привет, Хабр! Начну с того, что мне надоела убогость классов и наследования в JavaScript! Просидев тысячи часов над крупным JS-проектом, это стало для меня просто очевидным. Особенно когда переключаешься с бэкенда с использованием Yii2, на фронтенд. Ведь в Yii2 и php есть настоящие классы, настоящие protected/private поля, есть trait, всякие dependency injection и behavior. И вот сразу после всех этих штук, создаёшь такой файл NewClass.js для того чтобы написать какой-нибудь класс, и понимаешь, что в JavaScript ничего этого нет. И даже более того, классы можно писать сотнями разных способов — прототипное/функциональное наследование, ES6 классы, и разные сахара с использованием внешних библиотек. Тут я сказал себе — "хватит это терпеть!".


Что нам предлагают в современных стандартах?


В ES6 появилась возможность описания классов более привычным для всех языков способом, с помощью синтаксиса class {}. Однако это скорее более привычная запись классов с использованием старого прототипного наследования, и в нём так и не появилось ни protected, ни privatе модификаторов доступа к свойствам класса. В новейшем ES2017 стандарте этого до сих пор и нет.


Велосипедим


Конечно, не хотелось быть собирателем велосипедов, и первое, что я сделал, прежде чем сесть за свой вариант библиотеки, я стал искать уже существующие решения. И, всё что будет описываться ниже, не моё открытие — раму для велосипеда уже нашёл в идеях других источников, и библиотеке mozart. Последнюю хотелось бы особо отметить, т. к. она послужила хорошей основой для дальнейшего развития идеи реализации почти настоящих классов.


Краткий обзор возможностей


Чтобы не превращать статью в пересказ README проекта, опишу лишь кратко список возможностей, и приведу пример использования, а ниже расскажу, как работает вся эта магия.


1) Public, protected и private доступ к членам класса
var Figure = Class.create(function ($public, $protected, _) {
    $public.x = 0;
    $public.y = 0;
    $protected.name = 'figure';
    $protected.init = function (x, y) {
        _(this).id = 123; // private
        this.x = x;
        this.y = y;
    };
    $protected.protectedMethod = function () {
        console.log('protectedMethod: ', this.id, this.name, this.self.x, this.self.y);
    };
    this.square = function (circle) {
        return 2 * Math.PI * circle.radius;
    }
});

var Circle = Class.create(Figure, function ($public, $protected, _) {
    $public.radius = 10;
    $public.publicMethod = function () {
        console.log('publicMethod: ', _(this).id, _(this).name, this.radius);
        _(this).protectedMethod();
    };
});

var circle = new Circle(2, 7);
circle.radius = 5;
circle.publicMethod(); // publicMethod: undefined figure 5 / protectedMethod: 123 figure 2 7
console.log(Circle.square(circle)); // 31.415926536

2) Простое и множественное наследование классов, вызов родительских методов через $super
var Layer = Class.create(function ($public, $protected, _) {
    $protected.uid = null;
    $protected.init = function () {
        _(this).uid = Date.now();
    }
});

var Movable = Class.create(function ($public, $protected, _) {
    $public.x = 0;
    $public.y = 0;
    $protected.init = function (x, y) {
        this.x = x;
        this.y = y;
    }
    $public.move = function () {
        this.x++;
        this.y++;
    }
});

var MovableLayer = Class.create([Layer, Movable], function ($public, $protected, _, $super) {
    $protected.init = function (x, y) {
        $super.get(Layer).init.apply(this, arguments);
        $super.get(Movable).init.apply(this, arguments);
    }
});

var layer = new MovableLayer(); // смотрите предыдущий пример
console.log(layer instanceof Layer, layer instanceof Movable); // true false
console.log(Class.is(layer, Layer), Class.is(layer, Movable)); // true true

3) Автоматическое создание геттеров/сеттеров
var Human = Class.create(function ($public, $protected, _) {
    $protected.birthday = null;
    $public.getBirthday = function () {
        return _(this).birthday;
    };
    $public.setBirthday = function (day) {
        _(this).birthday = day;
    };
    $public.getAge = function () {
        var date = new Date(_(this).birthday);
        return Math.floor((Date.now() - date.getTime()) / (1000 * 3600 * 24 * 365));
    };
});

var human = new Human();
human.birthday = '1975-05-01';
console.log(human.age);

4) Примеси
var SortableMixin = function ($public, $protected, _) {
    $public.sort = function () {
        _(this).data.sort();
    };
};

var Archive = Class.create(null, SortableMixin, function ($public, $protected, _) {
    $protected.init = function () {
        _(this).data = [3, 9, 7, 2];
    };
    $public.outData = function () {
        console.log(_(this).data);
    };
});

var archive = new Archive();
archive.sort();
archive.outData(); // [2, 3, 7, 9]

Разоблачаем фокус


Так как объекты в JavaScript не имеют никаких настроек доступов для его свойств, мы сможем сымитировать похожее на protected/private поведение, путём скрытия защищённых данных. При обычном функциональном наследовании это делается путём замыкания на самом конструкторе, а все методы создаются для каждого экземпляра класса:


var SomeClass = function () {
    var privateProperty = 'data';
    this.someMethod = function () {
        return privateProperty;
    };
};
var data = [];
for (var i = 0; i < 10000; i++) {
    data.push(new SomeClass());
}

При выполнении данного кода в памяти создадутся помимо самих объектов, ещё 10000 функций someMethod, что сильно откушает память. При этом нельзя так просто вынести объявление функции за пределы конструктора, так как в этом случае функция потеряет доступ к privateProperty.


Для решения данной проблемы, нам нужно объявлять функцию метода лишь один раз, а получать защищённые данные только за счёт указателя на объект this:


var SomeClass;
(function () {
    var privateData = [];
    var counter = -1;
    SomeClass = function () {
        this.uid = ++counter;
    };
    SomeClass.prototype.someMethod = function () {
        var private = privateData[this.uid];
    };
})();

Так уже лучше, но всё-таки плохо. Во-первых, извне становится доступен некий идентификатор uid. А во-вторых, сборщик мусора никогда не очистит то, что попадёт в массив privateData и будет медленно но верно отжирать память. Для решения сразу двух проблем в ES6 появились замечательные классы Map и WeakMap.


Map — это почти те же массивы, но в отличие от них, в качестве ключа можно передать любой объект JavaScript. На для нас будут более интересны WeakMap — это тоже что и Map, но в отличие от него, WeakMap не мешает сборщику мусора очищать объекты, которые попадают в него.


Перепишем:


var SomeClass;
(function () {
    var privateData = new WeakMap();
    SomeClass = function () {};
    SomeClass.prototype.someMethod = function () {
        var private = privateData.get(this);
    };
})();

Так мы получили private. С реализацией protected всё гораздо сложнее — для хранения защищённых данных их нужно разместить в неком общем хранилище для всех производных классов, но при этом давать доступы для конкретного класса не для всех свойств, а только те, что объявлены в нём самом. В качестве такого хранилища мы опять используем WeakMap, а в качестве ключа — прототип объекта:


SomeClass.prototype.someMethod = function () {
    var protected = protectedData.get(Object.getPrototypeOf(this));
};

Для ограничения доступа только к тем protected-свойствам, которые есть в самом классе, мы будем выдавать классу не сам объект с защищёнными данными, а связанный объект, нужные свойства которого будут получаться из основного объекта, путём объявления геттера и сеттера:


var allProtectedData = { notAllowed: 'secret', allowed: 'not_secret' };
var currentProtectedData = {};
Object.defineProperties(currentProtectedData, {
    allowed: {
        get: function () { return allProtectedData.allowed; },
        set: function (v) { allProtectedData.allowed = v; },
    }
});
currentProtectedData.allowed = 'readed';
console.log(allProtectedData.allowed, currentProtectedData.allowed, currentProtectedData.notAllowed); // readed readed undefined

Вот примерно как-то так это работает.


img


Ну а дальше осталось лишь обвесить всё это красотой и возможностями, и вуаля!


Заключение


Подробное описание возможностей вы найдёте в README проекта. Всем спасибо за внимание!


> Проект на GitHub

Поделиться с друзьями
-->

Комментарии (61)


  1. Sirion
    09.01.2017 05:21
    +1

    Здесь должна быть картинка про 14 конкурирующих стандартов, но её вырезала полиция банальности.

    З.Ы. Кажется, я где-то уже видел JS классы на викмапах, но искать лень.


    1. PaulZi
      09.01.2017 08:12
      +1

      Если 14 стандартов не предоставляют того, что перечислено в заголовке статьи, то почему бы и нет.


  1. Andreyika
    09.01.2017 08:01
    +6

    Если вот такое придется писать

    var Archive = Class.create(null, SortableMixin, function ($public, $protected, _) {
        $protected.init = function () {
            _(this).data = [3, 9, 7, 2];
        };
        $public.outData = function () {
            console.log(_(this).data);
        };
    });
    


    то «у Билла» (который на картинке) в typescript все это (кроме множественного наследования) есть и работает проще и выглядит красивее (код разрабатываемого приложения, а не самого компилятора).


    1. PaulZi
      09.01.2017 08:15
      -3

      Если не ошибаюсь, в TypeScript все эти protected будут protected только до стадии компиляции включительно, в runtime же все это теряется.


      1. rMX
        09.01.2017 09:58
        +2

        А в чем конкретно глубокий смысл наличия protected свойств/методов в рантайме? Вроде как эти вещи нужны лишь для удобства разработки, не более.


        1. PaulZi
          09.01.2017 10:01

          Например, в том, что стороннему разработчику, чтобы расширить ваш класс, не нужно изучать TypeScript и при этом, соблюдать принципы инкапсуляции.


          1. mayorovp
            09.01.2017 10:07
            +9

            Лучше уж выучить TypeScript, чем чужую библиотеку где классы создаются через Class.create(null, SortableMixin, function ($public, $protected, _) {


          1. rMX
            09.01.2017 11:53

            Вопрос был именно про рантайм. Зачем в рантайме это нужно?
            Зачем тогда protected, если не для соблюдения инкапсуляции?

            Все это не наезда ради, просто хочется разобраться, для чего может потребоваться данная библиотека и чем она лучше других решений, помимо бонуса множественного наследования (сомнительного, на мой взгляд).


            1. PaulZi
              09.01.2017 12:05
              -1

              Собственно, нужно для того, чтобы можно было на нативном JS писать почти настоящие классы, без необходимости в TypeScript. Всё таки JavaScript !== TypeScript. И если в каждом браузере работал TypeScript и все фронтенд разработчики писали на нём, конечно, всё было бы прекрасно.
              Согласен, что для тех, кто пишет на TypeScript всё это не нужно.


              1. raveclassic
                09.01.2017 12:13
                +1

                В нативном JS нет классических классов и не может быть из-за прототипов. То, что ввели в ES6 — это просто сахар. С этим ничего не поделать и делать ничего не нужно. Если вам нужны «почти настоящие классы», лучше взять другой язык, который в них умеет.


                1. PaulZi
                  09.01.2017 12:19

                  Ну подытожив всё написанное в комментариях — хочешь писать хорошо и без велосипедов, пиши на TypeScript или не используй protected.


                  1. raveclassic
                    09.01.2017 12:23
                    +2

                    Мы в команде используем jsdoc и соглашение об именовании приватных полей через "_":
                    class Disposable {
                    /**
                    * @type {Boolean}
                    * @protected
                    */
                    _isDisposed;
                    }

                    IDE прекрасно подхватывает


                    1. LexS007
                      09.01.2017 18:51
                      +1

                      Самое адекватное решение, без лишних велосипедов. В Python подобный способ инкапсуляции уже давно существует.


                      1. k12th
                        09.01.2017 19:15

                        В Python нет такого количества людей, у которых это второй язык типа «ну и интерфейс там сваяешь на JS, че там, тыжвебразработчик».


    1. raveclassic
      09.01.2017 11:21
      +1

      Ну, кстати, множественное наследование можно сделать через миксины/декораторы


  1. vintage
    09.01.2017 08:40
    +6

    Вам этот приват и протектед что в итоге дали? Отладку упростили? Среда разработки подчёркивает ошибки в именах методов?


    Пример с примесями странный. Как отнаследоваться и примешать две примеси?


    1. raveclassic
      09.01.2017 09:33
      +3

      Ну как же, в Yii2 же есть, значит и в js надо притащить.


  1. gro
    09.01.2017 08:41
    +12

    Да, я тоже делал такое.
    Да, вообще все делали такое.
    Лет пять-десять назад.
    Я потом я сделал над собой усилие и перешёл на TypeScript.


  1. mayorovp
    09.01.2017 09:07
    +1

    У реализации приватных полей через WeakMap есть две проблемы. С циклическими ссылками и с просмотром значений "полей" в отладчике.


    Почему вы не стали использовать символы?


    var SomeClass = function() {
      var privateSymbol = Symbol("privateData");
    
      function SomeClass(value) {
        this[privateSymbol] = value;
      }
      SomeClass.prototype[privateSymbol] = 0;
      SomeClass.prototype.someMethod = function () {
        return this[privateSymbol];
      }
      return SomeClass;
    }()

    Вот так символ выглядит в консоли Хрома:



    1. PaulZi
      09.01.2017 09:16

      А будет ли сборщик мусора чистить protected-данные, хранящиеся в массиве за таким символом?


      1. mayorovp
        09.01.2017 09:40

        Не могу сказать, поскольку слабо представляю себе реализацию protected. Вы ее как-то совсем в двух словах рассказали.


      1. ggrnd0
        09.01.2017 10:53
        +1

        Так здесь какого-то массива хранящего данные всех объектов.
        Данные хранятся в самом объекте и будут удалены при удалении самого объекта.


        1. PaulZi
          09.01.2017 11:43

          Спасибо, надо будет попробовать.


  1. SerafimArts
    09.01.2017 10:37

    del


  1. k12th
    09.01.2017 11:50
    +2

    Велосипед, конечно, богатый, с кучей возможностей. К сожалению, недостатки у него такие же, какие были у всех остальных:


    • Каждый фреймворк юзал свою такую вот библиотечку, стандарта де факто (как с jQuery) не получилось
    • Поддержка в IDE, в силу динамической природы трюка, никакая, а в рантайме все эти private/protected ни к чему

    Присоединюсь к советующим посмотреть на TypeScript.


  1. RubaXa
    09.01.2017 12:03
    +7

    Уиии, 2017 год, а добрая традиция, или даже лучше сказать дисциплина, «пишем свои классы в JS» до сих пор живет!


  1. jbubsk
    09.01.2017 23:24

    TypeScript пожалуй лучше всего подчеркивает модификаторы доступа, тем более это важно именно при моделировании кода.


  1. dom1n1k
    09.01.2017 23:43
    +1

    > Особенно когда переключаешься с бэкенда с использованием Yii2, на фронтенд. Ведь в Yii2 и php есть настоящие классы
    Много лет назад я знакомился с php и меня выбесило отсутствие в нём перегрузки арифметических операций, как в C++. Я посчитал такое ООП неполноценным и забросил сей язык. Забросил не только поэтому, но этот эпизод стал важной последней каплей. Такие вот превратности судьбы.


    1. Free_ze
      10.01.2017 10:03
      +1

      Как перегрузка операторов относится к ООП?


      1. k12th
        10.01.2017 10:43

        Так же, как private/protected:)


        1. PaulZi
          10.01.2017 11:37

          Перегрузка — да, вещь хорошая, но необходима в меньшей степени, чем private/protected.
          Предложите в каком-нибудь сообществе типа java или php отказаться от private/protected — вам покрутят у виска. В JS-сообществе же с точностью наоборот.


          1. k12th
            10.01.2017 11:44
            +1

            А предложите в питоньем сообществе private/protected — покрутят точно так же. Вопрос привычки и удобства, реальной необходимости ни в перегрузке, ни в сокрытии нет.


            1. dom1n1k
              10.01.2017 15:41

              Когда нет перегрузки операторов, матричная или комплексная арифметика выглядит как-то так:
              a.add(b); // a, b - операнды

              или так
              mat.add(a, b); // mat - объект, играющий роль неймспейса

              а могла бы вот так
              a + b


              1. k12th
                10.01.2017 15:44

                Да, понятно, что так красивее и удобнее и приятнее, и мне тоже не хватает иногда перегрузки операторов в JS. Но решающей принципиальной разницы нет, далеко не в каждой задаче есть матрицы или комплексные числа.


              1. vintage
                10.01.2017 20:37
                +1

                a * b — это векторное, скалярное или поэлементное умножение?


                1. dom1n1k
                  10.01.2017 21:20
                  -1

                  Ответ очевиден — на усмотрение конкретной реализации.


                  1. vintage
                    10.01.2017 21:41
                    +1

                    Речь о том, что перегрузка операторов без возможности объявления своих операторов — так себе решение. Чего только стоит перегрузка битового сдвига (<<) для стримов в C++.


                    1. dom1n1k
                      11.01.2017 02:09

                      Нормальное при разумном подходе и чувстве меры. В C++ есть миллион способов прострелить себе ногу, так что из того?


                    1. Free_ze
                      11.01.2017 17:18

                      Чего только стоит перегрузка битового сдвига (<<) для стримов в C++.

                      Вы так говорите, будто это что-то плохое.


                      1. vintage
                        11.01.2017 17:20
                        +1

                        А что хорошего в том, что ты не знаешь что делает оператор пока не пройдёшься по всему дереву наследования в поисках всех его перегрузок?


        1. Free_ze
          10.01.2017 13:02
          -1

          Надеюсь, что вы шутите. private/protected в отрыве от ООП представить сложно.


          1. SerafimArts
            10.01.2017 13:41
            +1

            В случае с protected — соглашусь, но вот private… Никто не мешает использовать процедурный или функциональный язык, где private определяет наличие или отсутствие какой-то структуры (например функции) при экспорте модуля.


            … Ничего не напоминает? Модификаторы доступа — это лишь одна из возможностей реализации инкапсуляции, в JS инкапсуляция реализуется наличием/отсутствием кейворда export, например. Замените export на public, а его отсутствие на private, вот и получаем ту же самую инкапсуляцию на уровне модулей, а не на уровне класса/объект, что, к слову, местами профитнее (код интерфейса (не interface, а публичной части) тупо чище).


            1. Free_ze
              10.01.2017 17:03
              -1

              А давайте не будем заменять «папу» на «маму» и будем читать ветку полностью? Речь шла конкретно о ключевых словах protected/private и возможности их использовании вне ООП и даже объектного подхода.


              1. mayorovp
                10.01.2017 17:15

                А давайте не будем принимать совпадения за закономерности? Сегодня "вне объектного подхода" таких ключевых слов нет — а завтра кто-нибудь придумает новый чистый функциональный язык программирования, где эти слова будут присутствовать.


                1. Free_ze
                  10.01.2017 17:21

                  Изначально я отвечал на конкретную реплику и лишь в ее контексте. Далее понабигали любители холиваров, пытаясь возводить каждое мое слово в абсолют.

                  Не надо так.


                  1. SerafimArts
                    10.01.2017 17:40

                    Ну не правда же, я просто провёл параллель с JS и намекнул, что private есть и в JS, просто он иначе называется и иначе работает, а заодно сделал намёк, что этот "private" есть и в других не ОО языках, иногда даже так и называется, например в Ada или Fortran


                    UPD. И согласился по поводу protected, т.к. не припомню не ОО языков, где был бы подобный модификатор, хотя придумать можно.


                    1. Free_ze
                      10.01.2017 17:50

                      Вы шутите, да? Я все это время рассказывал, что в C++ protected/private имеют отношение к ООП, а перегрузка операторов — нет.

                      Весь остальной флуд меня изумляет своей беспочвенностью =)


                      1. SerafimArts
                        10.01.2017 18:18

                        Ну с моей стороны диалог выглядит так (насколько я его понял):


                        • Доминик: У каждого языка, мол, свои плюшки, мне, например, не понравился ООП в пыхе, т.к. там нет перегрузки операторов.
                        • Вы: Перегрузка операторов — это не ООП
                        • k12th: Так статья тоже не про ООП, а просто частности переноса одного в другое
                        • Вы: Да ладно, модификаторы как раз про ООП.

                        А дальше моё замечание, мол, ну почему же. Если я кого-то не совсем правильно понял, прошу понять и простить =)


  1. zxcabs
    10.01.2017 12:27
    +2

    Вот серьезно, такие библиотеки должны умереть.
    Мало вам классов в JS берите Dart там есть все и даже больше, без вот этих ваших костылей и прочего.


    1. PaulZi
      10.01.2017 14:05
      -1

      Dart только сам уже почти мёртв.
      Из реальных альтернатив только TypeScript, с его реализацией всего этого дела на этапе компиляции.
      Согласен, что велосипед и очередная библиотека. Лично для меня весь этот проект — показатель того, что на чистом JS можно реализовать настоящий protected, пусть и несколько нетривиально.


      1. raveclassic
        10.01.2017 14:19

        Вас сейчас Wrike заминусует


      1. zxcabs
        10.01.2017 15:35
        +1

        я не стал бы говорить что Dart мертв, насколько мне видно, он все так же развивается и поддерживает. Другой вопрос что он не получил популярности и у него не очень понятное будущее. Но это куда лучше чем вот такая библиотека, потому что будущее этой библиотеки еще туманней чем у Darta.

        Я так и не понял зачем вам настоящие приватные и защищенные методы в js? Для контроля кода в момент разработки достаточно писать jsdoc и тогда нормальная IDE сама сообщит что это за метод и как его следует использовать. Для иллюзии защищенности кода в момент выполнения, так это только иллюзия и лишнее переосложнение системы, еще не раз будет напоминать себе при отладке.


        1. PaulZi
          10.01.2017 16:04
          -1

          По поводу необходимости protected. Взгляните на какой-нибудь класс, реализующий, например, ActiveRecord. Protected очень помогает отделять методы которые можно вызывать из любого места, от тех, которые можно переопределить только расширяя класс. Наследование без protected это большой недостаток ООП в этом языке. Я бы не стал говорить что они не нужны.
          Другое дело, что JS из коробки это не предоставляет, а обходные пути либо сложные (как представлено здесь), либо работают только в определённой среде (предупреждения IDE, ошибки компиляции TypeScript). Проблема в том, что большинство библиотек написаны на чистом JS, а не на TypeScript. И не факт, что все работают в IDE с соблюдением всех рекомендаций.


          1. raveclassic
            10.01.2017 16:13
            +2

            Ну почему все пытаются воткнуть в JS ООП (в терминах Java/C++)? Тут прототипное наследование. Какие protected? Ну нет их тут и быть не может.
            Если вы хотите, чтобы вам компилятор жаловался, что вы обращаетесь к private/protected полю снаружи, так это вам компилируемый язык нужен, при чем тут JS-то вообще?


            1. Free_ze
              10.01.2017 17:15
              -1

              Ну почему все пытаются воткнуть в JS ООП (в терминах Java/C++)? Тут прототипное наследование.

              Потому что прототипное наследование в реализации JS — это грусть и погибель для поддержки более или менее сложных приложений, очевидно. Тренд в JS сейчас — это спрятать реальный, злой JS поглубже, обложившись разного рода трансляторами.


              1. LexS007
                11.01.2017 15:37
                +1

                В чем проблема? Просто не пишите на js, сейчас достаточно транс-компилируемых языков.
                Если кому-то не нравится ASM, то он на нем и не пишет, с js тоже самое.


                1. Free_ze
                  11.01.2017 17:16

                  В чем проблема? Просто не пишите на js

                  Проблема в том, что не писать на js в некоторых областях — удовольствие того же уровня, что и писать на нем. Как такой код дебажить, например? Производительности это так же не добавит, ибо js — это совсем не asm.

                  транс-компилируемых языков

                  Транслируемых же, на выходе обычный текст.


                  1. LexS007
                    11.01.2017 18:24

                    Как такой код дебажить, например?

                    Можно дебажить с включенным «Source Maps».

                    Транслируемых же, на выходе обычный текст.

                    Для source-to-source есть отдельные термины


          1. zxcabs
            10.01.2017 21:28
            +1

            Я все же с Вами не соглашусь.
            Мое мнение все же останется прежним, что правильней это помечать jsdoc`ом приватные/защищенные методы, чем реализовывать это через библиотеки.
            Что касается сторонних библиотек, то это проблема библиотек что там комментариев к коду нет, значит библиотека «не очень» и лучше воздержаться от ее использования.
            Что касается IDE то не надо думать о всех, нужно думать о себе. Настройте IDE что бы оно понимало jdoc, помечайте методы jsdoc'ом, а те кто пишут в блокноте должны либо читать ваш код и помнить головой где какой метод или использовать IDE.


          1. zxcabs
            10.01.2017 21:36
            +1

            добавление к предыдущему моему комментарию:
            Так же можно ввести договоренность в именовании методов, например:
            Методы которые начинаются на "$" это приватные, а "$$" это защищенные.

            И это во много раз проще в поддержке и отладке, чем копаться потом в стектрейсе который будет захламлен библиотечными функциями.
            Так же это проще для стороннего человека, ему не нужно въезжать в детали реализации библиотеки, достаточно только узнать о принятой методологии именовании методов.


  1. titulusdesiderio
    10.01.2017 19:26

    Тоже велосипедил протектед но более суровый, без общего хранилища Object_defineProtectedProperty