Шаблоны программирования Javascript (часть II)

оригинал

Начало перевода

Открытый модуль

Основываясь на опыте использования паттерна «модуль», Christian сделал то что он назвал «открытый модуль» (Revealing Module). Как следует из названия он похож на паттерн «модуль», но чуть более структурирован и понятен, особенно когда речь идет о совместной разработке.
Во-первых, он так же основан на функции, которая определяется и сразу выполняется:

  1. var anchorChange4 = function () {}();

После этого определим все свойства и методы, не разделяя их на публичные и защищенные:

  1. // это private свойство
  2. var config = {
  3. colors: [ "#F63", "#CC0", "#CFF" ]
  4. }
  5. // это public метод
  6. var init = function () {
  7. var self = this;
  8. var anchors = document.getElementsByTagName("a");
  9. var size = anchors.length;

Теперь самое интересное. Выше, в описании паттерна «модуль», я уверен, вы заметили, что он возвращает объект со всеми публичными свойствами и методами. В Открытом модуле вы возвращаете только ссылки на те свойства и методы, которые хотите показать.

  1. return {
  2. // declare which properties and methods are supposed to be public
  3. init: init,
  4. changeColor: changeColor
  5. }

Только два метода должны быть публичными init (который делает все подготовительную работу) и changeColor (который вызываем, когда щелкаем по ссылке). Глядя на код сразу видно какие свойства и методы общедоступны. Окончательный код выглядит следующим образом:

  1. // revealing module pattern
  2. var anchorChange4 = function () {
  3. // this will be a private property
  4. var config = {
  5. colors: [ "#F63", "#CC0", "#CFF" ]
  6. }
  7. // this will be a public method
  8. var init = function () {
  9. var self = this; // assign reference to current object to "self"
  10. // get all links on the page
  11. var anchors = document.getElementsByTagName("a");
  12. var size = anchors.length;
  13. for (var i =0; i < size; i++) {
  14. anchors[i].color = config.colors[i];
  15. anchors[i].onclick = function () {
  16. self.changeColor(this, this.color); // this is bound to the anchor object
  17. return false;
  18. };
  19. }
  20. }
  21. // this will be a public method
  22. var changeColor = function (linkObj, newColor) {
  23. linkObj.style.backgroundColor = newColor;
  24. }
  25. return {
  26. // declare which properties and methods are supposed to be public
  27. init: init,
  28. changeColor: changeColor
  29. }
  30. }();

Как и в других паттернах, надо вызвать соответсвующую функцию в блоке script:

  1. <script type="text/javascript">
  2. anchorChange4.init();
  3. </script>

И снова пример для этого паттерна.

Объект

Создание нового объекта класса (его экземпляра) существует во всех объектно-ориентированных языках. Но Javascript скорее объекто-прототипированный, а не объектно-ориентированный язык. В Javascript вам надо вызвать конструктор вашего объекта с соотвествующими параметрами для создания объекта. В нем нет классов и подклассов, как в Java.
Во-первых, необходимо написать конструктор вашего объекта:

  1. var anchorChanger = function () {};

Мы оставили контруктор пустым (и это видно), но позже добавим вызов метода init этого объекта.
Используя прототип, мы можем добавить дополнительные свойства в объект, для того чтобы они сразу были доступны в нем. Я добавил 3 свойства: config, changeColor и init:

  1. anchorChanger.prototype.config = {
  2. colors: [ "#F63", "#CC0", "#CFF" ]
  3. }
  4. anchorChanger.prototype.changeColor = function (linkObj, newColor) {
  5. linkObj.style.backgroundColor = newColor;
  6. };
  7. anchorChanger.prototype.init = function () {
  8. var self = this;
  9. var anchors = document.getElementsByTagName("a");
  10. var size = anchors.length;
  11. for (var i =0; i < size; i++) {
  12. anchors[i].color = self.config.colors[i];
  13. anchors[i].onclick = function () {
  14. self.changeColor(this, this.color);
  15. return false;
  16. };
  17. }
  18. };

Как отмечает Mike West, использование прототипа более эффективно, чем добавление этих свойств «на лету» при каждом создании объекта. Окончательный объект выглядит следующим образом:

  1. // конструктор объекта
  2. var anchorChanger = function () {
  3. this.init();
  4. };
  5. anchorChanger.prototype.config = {
  6. colors: [ "#F63", "#CC0", "#CFF" ]
  7. }
  8. anchorChanger.prototype.changeColor = function (linkObj, newColor) {
  9. linkObj.style.backgroundColor = newColor;
  10. };
  11. anchorChanger.prototype.init = function () {
  12. var self = this;
  13. var anchors = document.getElementsByTagName("a");
  14. var size = anchors.length;
  15. for (var i =0; i < size; i++) {
  16. anchors[i].color = self.config.colors[i];
  17. anchors[i].onclick = function () {
  18. self.changeColor(this, this.color);
  19. return false;
  20. };
  21. }
  22. };

В документе надо создать новый экземпляр anchorChange:

  1. <script type="text/javascript">
  2. new anchorChanger();
  3. </script>

Пример тут.

Определение ленивых функций

Peter Michaux предложил паттерн, который он назвал ленивые функции. Этот паттерн обычно используется когда вам надо сделать вычисления только один раз, а потом использовать результаты многократно. Используя этот паттерн, вы можете быть уверены, что то что нужно сделать однократно, будет сделано однократно. В качестве примера Peter приводит объединеную для разных браузеров функцию прокрутки. В зависимости от браузера, функция прокрутки создается при первом ее выполнении.
В моем случае этот паттерн не дает каких-то реальных преимуществ, так как нет каких-либо объемных или сложных вычислений. Но как это работает в целом? В начале надо объявить вашу основную функцию:

  1. var anchorChange5 = function () {};

Теперь добавляем свойства, как и в предыдущих паттернах:

  1. var config = {
  2. colors: [ "#F63", "#CC0", "#CFF" ]
  3. };
  4. var anchors = document.getElementsByTagName("a");
  5. var size = anchors.length;
  6. for (var i =0; i < size; i++) {
  7. anchors[i].color = config.colors[i];
  8. anchors[i].onclick = function () {
  9. anchorChange5().changeColor(this, this.color);
  10. return false;
  11. };
  12. }

Все это, должно быть сделано только один раз и нет необходимости добавлять эти свойства в последующих вызовах этой функции. Чтобы использовать ленивое определение функции, ее необходимо переопределить:

  1. // переопределение функции для хранения исключительно changeColor
  2. anchorChange5 = function () {
  3. return {
  4. changeColor: function (linkObj, newColor) {
  5. linkObj.style.backgroundColor = newColor;
  6. }
  7. };
  8. };

Окончательный код с ленивым определением функции выглядит следующим образом:

  1. var anchorChange5 = function () {
  2. var config = {
  3. colors: [ "#F63", "#CC0", "#CFF" ]
  4. };
  5. var anchors = document.getElementsByTagName("a");
  6. var size = anchors.length;
  7. for (var i =0; i < size; i++) {
  8. anchors[i].color = config.colors[i];
  9. anchors[i].onclick = function () {
  10. anchorChange5().changeColor(this, this.color);
  11. return false;
  12. };
  13. }
  14. anchorChange5 = function () {
  15. return {
  16. changeColor: function (linkObj, newColor) {
  17. linkObj.style.backgroundColor = newColor;
  18. }
  19. };
  20. };
  21. };

Что при этом происходит:

1. anchorChange5 при вызове устанавливает переменную config и собирает все ссылки на странице;
2. Назначается событие onclick для ссылок, на которое будет вызываться метод changeColor в переопределенной функции anchorChange5
3. В конце концов anchorChange5 переопределяется и теперь хранит только метод changeColor, который общедоступен.

И опять вызываем функцию в документе:

  1. <script type="text/javascript">
  2. anchorChange5();
  3. </script>

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

Как уже было сказано выше это только мой взгляд на решения этой задачи. Я уверен, что есть и другие решения, но цель этой статьи была изучить, как эти методы работаю в целом. Не существует одного мнения как лучше решить эту задачу. На мой взгляд, здесь singleton является наиболее удобным решением. Он довольно компактен. Но для других, более сложных проблем, использование модулей или создание объектов или определение ленивых функций может быть более подходящим паттерном

Rate It! (Average 3.80, 5 votes)

Related Posts

0 Responses to Шаблоны программирования Javascript (часть II)

Leave a Reply

Mail will not be published