Отличная статья!
Идея (по крайней мере, для меня) новая, и, разумеется, имеет полное право на существование.
Но, ситуация, которую описывает Автор, скорее — ошибка проектирования, нежеле фича JavaScript. Это стоило бы отметить в статье. Сама статья может служить отличным гайдом, как исправить схожую ошибку проектирования на этапе программирования (когда все форматы для обмена и хранения данных утверждены, и нет возможности внести коррективы).
От себя хотел выделить правило: Никогда не используй название свойств объектов JS для хранения даннных. Свойства объектов используем только для структурирования данных.
Хранение данных в названиях свойств объектов равносильно хранению данных в названиях столбцов в таблицах реляционных СУБД.
Выше, в комментариях, обсуждалась технологическая проблема, в следствии которой приходилось держать отдельный массив для быстрого доступа к данным по сложному ключу или для сортировки этих данных. В теории СУБД такие массивы называются индексами. В использовании массивов такого назначения нет ничего плохого.
Если не нравится то, что приходится использовать несколько разных массивов с разными именами, один из самых производительных способов — создать свой класс, наследуемый от Array или Collection. Ну, и реализовать работу всех индексов в рамках этого класса.
Да, Вы правы по поводу Object.create().
Поротестировал много разных вариантов прототипов.
Результаты приблизительно одинаковы везде — 4 минуты ± 15 секунд.
Тест показывал неоптимизированность именно Object.create().
Сами тесты проводил самыми стандартными средствами: readline-интерфейс node и top с фильтром результатов по pid.
Есть еще один хороший сервис тестирования, но через браузер: jsperf.com/naked-vs-simple-objects
Тут надо быть вимательным, результаты отличаются в разных браузерах на разных ОС.
Также, по заявкам трудящихся на комментарий ниже, к утру потестирую вышеперечисленное на node v0.12.4.
Что примечательно, запустил один и тот же код по созданию млрд обычных объектов и млрд голых. Память оба процесса расходуют одинаково. А вот процессорное время голые съели заметно больше, чем обычные.
Обычные объекты создались за 43 с хвостиком секунды процессорного времени.
Голые съели больше 4х минут.
Скриншот прилагается. Node v0.10.26
По части curr вы правы. Для корректного понимания IDE можно добавить в комментарий @returns {function}. Ну, а как пользоваться сей конструкцией можно разъяснить в свободной форме в формате markdown в том же комментарии.
По поводу примисей и наследования я понял, что Вы хотели донести. Но все это можно реализовать и с util.inherits(). Разница лишь в том, что util.inherits(Client, Security) оставит методы и свойства Client, если таковые имеются в Security.
То есть, например, есть классы Security и Client. Если реализовать так, то методы и свойства Security будут поверх Client:
var SecurityClient = mixClass(Client, Security);
function mixClass(cl, mx) {
var resClass = function(){};
util.inherits(resClass, mx);
util.inherits(resClass, cl);
return resClass;
}
Как оформить сие добро в jsdoc не могу уверенно предложить. Но, могу с уверенностью сказать, может примиси и удобный для web инструмент (в браузере много стандартных классов, в которых можно расширять функционал: HTMLDocument, XMLHttpRequest), но в Node.JS я бы уверенно обходился бы без них, с базовым одноуровневым наследованием.
Я не против фабрик, но у каждого класса должно быть «жесткое» имя, закрепленное в своем namespace.
То есть, да, разумно к тому коду, что есть приделать возможность получения класса через фабрику. Это бы помогло в случае, например, если тип хранилища указан в конфиге. Но обязательно должна остаться возможность получить класс и по «жесткой» ссылке.
Классы я обычно определяю олдскул-способом Function().prototype.
Так, jsdoc шарится по всем файлам исходных кодов, а IDE тоже прекрасно понимает такие комментарии, структура кода всегда под рукой. Код готов к любому рефакторингу.
Кроме того, процессы node могут иметь такие состояния, когда их необходимо завершить или перезапустить:
idle: процесс использует CPU на 0% продолжительное время. Тут может быть два варианта: если процесс выполнял какую-то работу, скорее всего процесс по какой-то причине потерял callback или что-то в этом роде. Такой процесс необходимо перезагрузить. Если данный процесс реализует, например, http-сервер с каким-нибудь малопосещаемым сайтом, то скорее всего такое состояние обусловленно тем, что на сайт никто давно не заходил. Такой процесс можно завершить, а при запросе на сайт, снова по быстрому поднять http-сервер.
CPU-limit: процесс использует CPU на 100% (или свой ограниченный максимум) продолжительное время. Такой процесс скорее всего зациклился или попал на невыполнимый участок задачи. Такой процесс можно перезагрузить, чтобы сам не застревал на одной задаче и не мешал другим конкурирующим процессам.
RAM-limit: процесс использует максимум выделенной для него памяти. Не секрет, что node-процессы по ходу своего выполнения текут. Совсем ожиревшие процессы тоже можно перезагружать. Это напоминает борьбу с утечками методом PHP. — То есть, зачем удалять ненужные переменные?! — все переменные удаляются вместе с завершением процесса.
По началу, я тоже использовал одновременное асинхронное выполнение кода в несколько потоков. Но, код выполняется в одном процессе, соответственно, на одном ядре CPU. Для использования максимума ресурсов обычно используется оба подхода: код работает в несколько процессов, каждый процесс из которых выполняет код в несколько потоков.
Повышать количество потоков в каждом процессе может быть выгодно для экономии оперативной памяти, т.к. модули для второго и последующих потоков уже загруженны. Главное — не переборщить, во избежание зависания процессов. Но использование нескольких процессов тоже неизбежно.
Вы правы, можно обойтись и без Object.defineProperty().
Правда, я не использую в проектах фабрики из-за ненаглядности структуры кода, например, для IDE.
Так же мы используем общую документацию по всем проектам, пакетам, модулям. Для представления списка всех классов в древовидной форме, нам приходится лишь помогать jsdoc лишь указывая что есть @constructor, а что @namespace. Использование фабрик требовало бы модернизации сборщика/генератора документации.
Идея (по крайней мере, для меня) новая, и, разумеется, имеет полное право на существование.
Но, ситуация, которую описывает Автор, скорее — ошибка проектирования, нежеле фича JavaScript. Это стоило бы отметить в статье. Сама статья может служить отличным гайдом, как исправить схожую ошибку проектирования на этапе программирования (когда все форматы для обмена и хранения данных утверждены, и нет возможности внести коррективы).
От себя хотел выделить правило: Никогда не используй название свойств объектов JS для хранения даннных. Свойства объектов используем только для структурирования данных.
Хранение данных в названиях свойств объектов равносильно хранению данных в названиях столбцов в таблицах реляционных СУБД.
Выше, в комментариях, обсуждалась технологическая проблема, в следствии которой приходилось держать отдельный массив для быстрого доступа к данным по сложному ключу или для сортировки этих данных. В теории СУБД такие массивы называются индексами. В использовании массивов такого назначения нет ничего плохого.
Если не нравится то, что приходится использовать несколько разных массивов с разными именами, один из самых производительных способов — создать свой класс, наследуемый от Array или Collection. Ну, и реализовать работу всех индексов в рамках этого класса.
Node v0.12.4
Object.create()
.Поротестировал много разных вариантов прототипов.
Результаты приблизительно одинаковы везде — 4 минуты ± 15 секунд.
Тест показывал неоптимизированность именно
Object.create()
.Сами тесты проводил самыми стандартными средствами: readline-интерфейс node и top с фильтром результатов по pid.
Есть еще один хороший сервис тестирования, но через браузер: jsperf.com/naked-vs-simple-objects
Тут надо быть вимательным, результаты отличаются в разных браузерах на разных ОС.
Также, по заявкам трудящихся на комментарий ниже, к утру потестирую вышеперечисленное на node v0.12.4.
Обычные объекты создались за 43 с хвостиком секунды процессорного времени.
Голые съели больше 4х минут.
Скриншот прилагается. Node v0.10.26
curr
вы правы. Для корректного понимания IDE можно добавить в комментарий@returns {function}
. Ну, а как пользоваться сей конструкцией можно разъяснить в свободной форме в формате markdown в том же комментарии.По поводу примисей и наследования я понял, что Вы хотели донести. Но все это можно реализовать и с
util.inherits()
. Разница лишь в том, чтоutil.inherits(Client, Security)
оставит методы и свойства Client, если таковые имеются в Security.То есть, например, есть классы Security и Client. Если реализовать так, то методы и свойства Security будут поверх Client:
Как оформить сие добро в jsdoc не могу уверенно предложить. Но, могу с уверенностью сказать, может примиси и удобный для web инструмент (в браузере много стандартных классов, в которых можно расширять функционал: HTMLDocument, XMLHttpRequest), но в Node.JS я бы уверенно обходился бы без них, с базовым одноуровневым наследованием.
Так или иначе, все зависит от конкретной задачи.
Я не против фабрик, но у каждого класса должно быть «жесткое» имя, закрепленное в своем namespace.
То есть, да, разумно к тому коду, что есть приделать возможность получения класса через фабрику. Это бы помогло в случае, например, если тип хранилища указан в конфиге. Но обязательно должна остаться возможность получить класс и по «жесткой» ссылке.
Замыкания к процессу документирования вообще никакой гранью не могу отнести.
По поводу динамических примесей в прототипы. На Node.JS это реализованно
util.inherits()
.А в jsdoc множественное наследование можно описать:
@constructor
,@namespace
и остальные.Например, для обозначения namespace я добавлю в
index.js
:А для класса, например:
Классы я обычно определяю олдскул-способом
Function().prototype
.Так, jsdoc шарится по всем файлам исходных кодов, а IDE тоже прекрасно понимает такие комментарии, структура кода всегда под рукой. Код готов к любому рефакторингу.
Повышать количество потоков в каждом процессе может быть выгодно для экономии оперативной памяти, т.к. модули для второго и последующих потоков уже загруженны. Главное — не переборщить, во избежание зависания процессов. Но использование нескольких процессов тоже неизбежно.
Object.defineProperty()
.Правда, я не использую в проектах фабрики из-за ненаглядности структуры кода, например, для IDE.
Так же мы используем общую документацию по всем проектам, пакетам, модулям. Для представления списка всех классов в древовидной форме, нам приходится лишь помогать jsdoc лишь указывая что есть
@constructor
, а что@namespace
. Использование фабрик требовало бы модернизации сборщика/генератора документации.