Организация кода в публичной библиотеке
Здравствуйте.
В процессе обучения пишу скрипт (главная задача — именно учебная), который хоть и вряд-ли буду публиковать, но хотел бы, чтобы он был потенциально готов к публикации и распространению. Схема использования — создается объект с данными, модифицируется с помощью собственных методов, затем считывается итоговое значение. Ничего уникального. Но в связи с особенностями задачи — применяться может очень интенсивно, от десятков тысяч до миллионов вызовов. Возникла дилема — как организовать код, и с моей неопытностью я не могу принять взвешенное решение самостоятельно. Вариант первый — классическое прототипное наследование в чистом виде. Создаю конструктор, присваиваю прототипу объект с методами. Либо каждый метод в отдельности — Constructor.prototype.method = function() { ... }, чтобы не затереть уже имеющиеся поля, вроде conctructor. «Приватные» методы обозначаются символом в имени, по сути оставаясь публичными. Вариант второй. Функция, скрывающая приватные методы в замыкании, и возвращающая объект только с публичными методами. Попытался составить тесткейс для сравнения производительности этих подходов, сделал два тестовых «класса» с абсолютно одинаковой функциональностю, реализованные по-разному. По результатам моих измерений прототипное наслделование дает прирост в производительности, в зависимости от браузера, либо незначительный (4—5%), либо ощутимый (10—20% и больше). К недостаткам прототипного наследования могу отнести видимость приватных методов извне («захламление») и необходимость использовать new с конструктором (или определить функцию-обертку для этого). При создании замыканий получаем в некоторых условиях более медленный код, однако более «чистый» объект на выходе, без видимых приватных методов, и более простой в использовании (не требуется new или обертка). Прошу помощи в выборе. Также смущает то, что сам не встречал публичных библиотек или фреймворков, использующих чистое прототипное наследование. Спасибо. |
Цитата:
|
Частая ошибка новичков изучающих ООП: модификаторы видимости private и protected созданы ИСКЛЮЧИТЕЛЬНО для увеличения уровня абстракции и банальной удобности, а не для "сокрытия от злых хакеров".
Я использую классическое соглашение: _ private __ protected и всем советую |
Помоему такого рода "приватные методы" (ввиде локальных функций) не оставляют возможности наследоваться от класса и перекрывать методы.
Более того, такой способ работает только для функций (ибо функции расшарены для всех экземпляров), а как же быть с приватными свойствами? Кстати, никто не встречал использование вместо знака «_» знак «$» для приватных свойств/методов? |
Цитата:
Цитата:
|
Цитата:
function Animal() {
}
Animal.prototype.say = function() {
alert('i Animal')
};
Cat.prototype = Object.create(Animal.prototype); //наследуем
function Cat() {
}
Cat.prototype.say = function() {
this.constructor.prototype.say.call(this); //перекрытый метод
alert('i Cat')
};
new Cat().say();
можно еще для простоты сделать так
Cat.prototype = Object.create(Animal.prototype); //наследуем
Cat.prototype.parent = Cat.prototype;
function Cat() {
}
Cat.prototype.say = function() {
this.parent.say.call(this); //перекрытый метод
alert('i Cat')
};
Цитата:
|
Цитата:
Цитата:
Но для выбора в моем случае это всего лишь один из «недостатков». Также смущает необходимость использования new. В подобного рода скриптах, рассчитанных на массовое использование, мне подобное требование не встречалась (неопытность? такие библиотеки, скрипты существуют?) Цитата:
Да, и еще вопрос вдогонку. Те, кто использует классическое прототипное наследование, в каком формате вы присваиваете значение прототипу? Constructor.prototype = { … } или Constructor.prototype.field = …? |
Цитата:
Цитата:
Цитата:
Цитата:
|
Прототипы + соглашения + обертка для new. С другой стороны смотря для кого пишешь - можно и не оборачивать new. Тот, кто используют приватные методы извне - сам дурак :)
|
Цитата:
Цитата:
Цитата:
Цитата:
|
Цитата:
|
Цитата:
var obj = Constructor.create(params);
Constructor.create = function(params) {
return new Constructor(params);
};
|
Цитата:
К слову, если очень нужно запретить изменение метода, то можно использовать "заморозку" (seal, freeze, preventExtensions), но не понимаю зачем, ибо если челочек осмысленно юзает приватные методы извне, то он априори не прав. |
Насчет того, как принято в JS, кажется начинаю понимать :)
В принципе некоторый опыт с другими языками (в том числе ООП) был, но давно. И по сравнению с ними в JS все как-то непривычно, кажется, надо просто понять, что «тут это нормально». Кстати, где можно почитать насчет seal, freeze, preventExtensions и т. д. Использовать здесь точно не буду, просто чтобы быть в курсе... nerv_, это понятно, сам о таком думал, но особенности задачи таковы, что такая запись будет слишком громоздкой, предполагаю, что в коде чаще будет много коротких цепочек вызовов, и там такая длинная инициализация будет перегружать исходник и ухудшать читаемость. Если бы объекты создавались редко, а затем интенсивно модифицировались — можно было бы и так, но пока мне кажется, что именно для этой задачи — не очень красивый вариант. |
Цитата:
Цитата:
1.
'use strict';
function Waffle() {
alert(this === undefined);
this.tastes = 'yummy';
}
new Waffle();
Waffle();
В строгом режиме ES5 ссылка this больше не указывает на глобальный объ- ект. 2.Возвращать из конструктора "другой" объект:
function Waffle() {
var obj = Object.create(Waffle.prototype);
obj.tastes = 'yummy';
alert(obj instanceof Waffle);
return obj;
}
new Waffle();
Waffle();
3.Использовать конструктор вызывающий сам себя:
function Waffle() {
if (!(this instanceof Waffle)) {
return new Waffle();
}
this.tastes = 'yummy';
alert(this instanceof Waffle);
}
new Waffle();
Waffle();
*писал на скорую руку, мог ошибиться... |
Цитата:
|
Цитата:
|
nerv_, спасибо :) Первый способ не подходит, поскольку требует обязательного использования "use strict", но на будущее буду иметь в виду. Остальные — интересно.
Все-таки учиться только самостоятельно по источникам в учебниках, литературе не так эффективно, вот даже по простым вопросам общение с более опытными разработчиками сразу приносит ощутимую пользу. |
Пожалуй, придется поднять тему снова :(
Возник такой вопрос (опять же из разряда «как принято поступать в таких случаях»). Опять же не знаю, пригодится оно мне в итоге или нет, но разобраться в любом случае хочется. В корневом объекте, который объявлен в скрипте, есть поля, в свою очередь хранящие структурированную информацию. Например, координаты
{ x: 1, y: 2}
. Сейчас, чтобы проверить, что там действительно находятся координаты, иногда приходится использовать проверки типа
if (this.coord && typeof this.coord.x == 'number' && typeof this.coord.y == 'number') {…}
Например, если данные получены извне и еще не провалидированы. Есть мысль объявить для таких объектов конструкторы и проверять
if (this.coord instanceof Coord) {…}
Валидировать можно при инициализации объекта, например. Опять возникает проблема засорения пространства имен. Библиотека уже объявляет корневой объект (например, LibraryRoot, сам является конструктором), и не хочется помимо него вводить отдельные конструкторы. Пока приходит в голову только объявлять конструкторы как LibraryRoot.Coord, но что-то меня смущает в этом варианте. Собственно, вопрос, нормально ли это или есть способы получше? |
LibraryRoot.Coord. Нормально, много так где сделано.
|
Спасибо :)
|
| Часовой пояс GMT +3, время: 16:59. |