Javascript.RU

Часть 1. Улучшаем веб-разработку. Дружелюбный javascript

Порой сочетание javascript-html поражает своими возможностями. Возможно, уже не далеко будущее, в котором большинство приложений будет работать из-под браузера. Здесь и офис, и игры, и мультимедиа, и облачные вычисления. Чем масштабнее приложение, тем больше усилий оно требует при разработке.

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

Использовать будем AWeb-library. Библиотека написана на чистом javascript и использует возможности только html-javascript-css. Полный обзор библиотеки отложим на потом, рассмотрим лишь ее небольшую часть по работе с javascript-объектами.

Для подключения библиотеки необходимо в блоке head вашей html-страницы дописать:

<script type="text/javascript" src="http://a-web.me/scripts/a-web.js"></script>

Напишем наше первое приложение. Стартовой функцией проекта, по аналогии с языками C/C++, является функция main. Она будет вызвана после загрузки HTML кода страницы, загрузки javascript'ов и стилей. Не нужно подстраиваться под браузеры, обрабатывать различные события. Все очень просто.

Пример функции, показывающей сообщение: "Hello, world".

function main() {
   alert( "Hello, world" );
}

Если вы пишете, например, свою библиотеку, и вам необходимо из разных частей программы определить действия, связанные с загрузкой страницы, то можно воспользоваться функцией AWeb.ready.

Значительно переработан процесс создания классов. Появился уровень абстракции, возможности и синтаксис которого приближен к языкам C++/Java.

Класс реализуется через обертку AWeb.class. Может содержать public, protected и private методы.

  • Public-методы/свойства могут быть вызываны как из методов своего класса, так и из методов других классов или внешних функций
  • Protected-методы могут быть вызываны как из методов своего класса, так и из методов классов наследников. Из других классов или внешних функции protected-методы не доступны.
  • Private-методы/свойства могут быть вызваны только из методов своего класса. Из других классов или внешних функций private-методы не доступны.

Опишем простой класс "A", имеющий:

  • Public-метод getInfo
  • Private-метод incCalls
  • Private-свойства variable1, calls
/**
  * A-class
  */
var ClassA = AWeb.class({
   public : {
      /**
        * A-class constructor
        */
      constructor : function() {
         /* Private variable */
         this.variable1 = "A";
         this.calls = 0;
      },
      
      /**
        * Function returns information about the object
        */
      getInfo : function() {
         this.incCalls();
         return "variable1=" + this.variable1 + ", calls=" + this.calls;
      }
   },
   private : {
      /**
        * Private function
        */
      incCalls : function() {
         this.calls++;
      }
   }
});


/**
  * Main project function
  */
function main() {
   var a = new ClassA();
   
   alert(
      "a.getInfo(1)=" + a.getInfo() + "\n" +
      "a.getInfo(2)=" + a.getInfo() + "\n" +
      "a.variable1="  + a.variable1 + "\n" +
      "a.incCalls="   + a.incCalls
   );
}

Конструктор любого класса описывается функцией constructor. Public, protected и private-методы описываются каждый в своем блоке. Конструктор класса обязательно должен быть описан в блоке public.

Переменная this в каждом методе класса содержит внутренние (private) переменные класса. Таким образом, написав выражение this.variable1 = "A", мы создали private-переменную, которую может читать и изменять любой метод класса, но которая не видна вне методов класса.

Результатом работы указанного примера будет:

a.getInfo(1)=variable1=A, calls=1
a.getInfo(2)=variable1=A, calls=2
a.variable1=undefined
a.incCalls=undefined

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

  • Read-write
  • Readonly
  • Writeonly

Создадим класс "B", в котором определены свойства: secretKey и loadedSign.
Значением свойства secretKey пользователь управляет сам. Значение свойства loadedSign пользователь изменить не может, оно только для чтения. Значение свойства loadedSign становится равным истине при первом изменении свойства secretKey.

/**
  * B-class
  */
var ClassB = AWeb.class({
   public : {
      /**
        * A-class constructor
        */
      constructor : function() {
         this.key = "<empty>";
         this.loaded = false;
      },
      
      /**
        * Read-write. Contains user's secret key
        */
      secretKey : {
         get : function () {
            return this.key;
         },
         set : function( value ) {
            this.key = value;
            this.loaded = true;
         }
      },
      
      /**
        * Readonly. True if loaded
        */
      loadedSign : {
         get : function() {
            return this.loaded;
         }
      }
   }
});

/**
  * Main project function
  */
function main() {
   var b = new ClassB();
   
   alert( b.secretKey + " " + b.loadedSign );
   
   b.secretKey = "<new>";
   alert( b.secretKey + " " + b.loadedSign );
}

Результатом работы указанного примера будет:


<empty> false
<new> true

Для создания класса-наследника используется ключевое слово extends в описании класса. Унаследовать можно свойства и методы только одного класса.
Класс наследник получает доступ ко всем public и protected методам родительского класса. При этом protected методы родительского класса становятся private-методами дочернего класса.

Создадим класс "C", являющийся наследником класса "A". В классе "A" определим protected-функцию changeVariable1, а в классе "C" определим public-функцию getLongInfo.

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

/**
  * A-class
  */
var ClassA = AWeb.class({
   public : {
      /**
        * A-class constructor
        */
      constructor : function() {
         /* Private variable */
         this.variable1 = "A";
         this.calls = 0;
      },
      
      /**
        * Function returns information about the object
        */
      getInfo : function() {
         this.incCalls();
         
         return "variable1=" + this.variable1 + ", calls=" + this.calls;
      }
   },
   protected : {
      /**
        * Protected function
        */
      changeVariable1 : function( value ) {
         this.variable1 = value;
      }
   },
   private : {
      /**
        * Private function
        */
      incCalls : function() {
         this.calls++;
      }
   }
});


/**
  * C-class
  */
var ClassC = AWeb.class({
   extends : ClassA,
   public : {
      /**
        * B-class constructor
        */
      constructor : function() {
         this.super();
         this.changeVariable1( "C" );
      },
      
      /**
        * Function returns extended information about the object
        */
      getLongInfo : function() {
         return this.incCalls !== undefined ? "incCalls visible" : "incCalls undefined";
      }
   }
});


/**
  * Main project function
  */
function main() {
   var a = new ClassA(),
       c = new ClassC();
       
   alert(
      "a instanceof ClassA: " + (a instanceof ClassA) + "\n" +
      "a instanceof ClassC: " + (a instanceof ClassC) + "\n" +
   
      "a.getInfo " + (a.getInfo ? "exists" : "undefined") + "\n" +
      "a.getLongInfo " + (a.getLongInfo ? "exists" : "undefined") + "\n" +
      "a.changeVariable1 " + (a.changeVariable1 ? "exists" : "undefined") + "\n" +
      "a.getInfo()=" + a.getInfo() + "\n\n" +
      
      "c instanceof ClassA: " + (c instanceof ClassA) + "\n" +
      "c instanceof ClassC: " + (c instanceof ClassC) + "\n" +
      
      "c.getInfo " + (c.getInfo ? "exists" : "undefined") + "\n" +
      "c.getLongInfo " + (c.getLongInfo ? "exists" : "undefined") + "\n" +
      "c.changeVariable1 " + (c.changeVariable1 ? "exists" : "undefined") + "\n" +
      
      "c.getInfo()=" + c.getInfo() + "\n" +
      "c.getLongInfo()=" + c.getLongInfo()
   );
}

Результатом работы кода будет:


a instanceof ClassA: true
a instanceof ClassC: false
a.getInfo exists
a.getLongInfo undefined
a.changeVariable1 undefined
a.getInfo()=variable1=A, calls=1


c instanceof ClassA: true
c instanceof ClassC: true
c.getInfo exists
c.getLongInfo exists
c.changeVariable1 undefined
c.getInfo()=variable1=C, calls=1
c.getLongInfo()=incCalls undefined

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

По аналогии с языком Java можно организовать множественное наследование классов-интерфейсов. В этом случае класс наследник должен реализовать все методы, описанные в классе интерфейсе.

Есть еще полезные особенности вида:

  • Уникальный идентификатор типа класса
  • Уникальный идентификатор экземпляра класса

Но об этом можно написать отдельно.

По сравнению с классическим javascript описывать объекты стало значительно проще. Появились визуально понятные конструкции описания классов, появились области видимости в классе, появились полноценные свойства классов.

В итоге, использование данной библиотеки позволит:

  • повысить качество кода
  • уменьшить время на разработку и сопровождение кода
+1

 
Поиск по сайту
Другие записи этого автора
Больше записей нет. Прокомментируйте эту запись - может быть, тогда он что-нибудь еще хорошее напишет ;)
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Популярные таги
Последние темы на форуме
Forum