Часть 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 описывать объекты стало значительно проще. Появились визуально понятные конструкции описания классов, появились области видимости в классе, появились полноценные свойства классов.
В итоге, использование данной библиотеки позволит:
- повысить качество кода
- уменьшить время на разработку и сопровождение кода
|