Главная · Карта сайта · Поиск · Статьи · Компьютерные курсы · Обучающие программы · Открытые проекты · Веб-программирование · Создание интернет-сайта · Полезные ссылки · Глоссарий · Контакты · Декабрь 09 2016 20:20:59
Последнее опубликованное

Что такое Model-View-Controller
Pattern Model-View-Controller

Как создать свой веб-сайт
Как создать свой сайт в интернете

Разное
Статистика

Основы веб-дизайна. Программирование на JavaScript


JavaScript

В этом разделе мы продолжим рассматривать различные техники и технологии веб-дизайна, но уже больше в контексте веб-программирования на стороне клиента. В предыдущем разделе, помимо особенностей HTML/XHTML разметки и настроек CSS, в некоторых примерах использовался программный код на языке JavaScript. Тогда я не уделял особого внимания этим программкам, поскольку внешне JavaScript мало чем отличается от других языков программирования и, в частности, по синтаксису практически близнец языку Java, хотя Java и JavaScript – это совсем разные языки программирования.

JavaScript - это встроенный в интернет браузер язык программирования, который поддерживает большинство основных конструкций процедурных языков программирования: составной оператор {}, цикл for, цикл while, конструкцию для обработки исключительных ситуаций try-catch и некоторые другие, и поэтому, имея базовые представления о программировании совсем несложно разобраться в смысле той или иной процедуры, написанной на нем. Если вам знакомы эти конструкции, то с их воплощением на JavaScript вы без труда разберетесь на примерах. Если нет, то пройдите по ссылке, указанной выше и узнайте об их назначении.

На первом этапе дело обстояло именно так, но дальнейшие примеры потребуют от читателя знаний о некоторых особенностях этого замечательного скриптового языка программирования. Я не большой эксперт по части JavaScript, но могу провести сравнительный анализ и выделить наиболее важные моменты. Итак, начнем.

Подключение программы на JavaScript

Для встраивания кода JavaScript в разметку интернет страницы предназначен отдельный тег <script>, который может содержать программный код непосредственно или подключать внешний файл через атрибут src. Если программный код объемный, или если вы работаете со стандартом XHTML, который, как известно, произошел от XML и поэтому очень трепетно относится к отдельным специальным символам, то удобней всего программу разместить в отдельном *.js файле или файлах (тегов <script> может быть несколько). В своих примерах JavaScript код я вставляю прямо в разметку страницы, поскольку, для наглядности в демонстрационных целях считаю такой способ наиболее подходящим. Обработка данных и определение сложных типов и процедур могут идти последовательно друг за другом, поскольку раздел определений в JavaScript не отделен явно от основной части программы в отличие от того же Pascal/Delphi. Все, приводимые в дальнейшем короткие примеры можно вставлять прямо в html код: либо в заголовке <head>, либо в любом другом месте основного содержимого страницы <body> следующим образом:

<script type="text/javascript">
   программный код JavaScript
</script>

Можно также вставлять код в текстовые файлы и подключать его примерно так:

<script type="text/javascript" src="путь к текстовому файлу *.js"></script>

Динамическая типизация и интерпретация программного кода

Сразу бросается в глаза основное отличие JavaScript от того же Java или C++ - при описании переменной не указывается ее тип. Дело в том, что тип переменной в JavaScript определяется автоматически в момент присваивания ей значения, и для такого поведения есть отдельный термин – динамическая типизация. Также, код JavaScript не компилируется перед тем, как быть исполненным, а последовательно обрабатывается встроенным в интернет браузер интерпретатором. По этой причине, если вы допустили в коде ошибку, например, обратились к переменной, которая не была ранее объявлена, то узнаете вы об этом только тогда, когда интерпретатор дойдет это этого места в программе. Притом, узнаете лишь косвенно, поскольку браузер просто не выполнит тех действий, которые предусматривает программа, начиная с того места, где закралась ошибка. Пример:

{
 alert('message1');
 alert(message2);
}

Первое сообщение вы увидите, а второе - нет, поскольку либо здесь забыли поставить кавычки, либо забыли определить переменную message2. Следующий код уже вполне себе работоспособный.

{
 alert('message1');
 var message2 = 'message2'; 
 alert(message2);
}

Но, тем не менее, интерпретатор перед началом исполнения программы проверяет ее на соответствие синтаксису JavaScript, и если код ему не соответствует, то ничего вообще не запускается и не выполняет. Пример:

{
 alert('message1');
 message2 = 'message2';
 if alert(message2);
}

Интерпретатор видит знакомое ему слово If, но не понимает, к чему оно здесь, поскольку самого условия я не указал. Приведенный выше код не обрабатывается браузером полностью, а следующий пример хоть и бессмысленный, но абсолютно корректный.

{
 alert('message1');
 message2 = 'message2';
 if (true) alert(message2);
}

Объекты и функции

Как только вы сталкиваетесь с задачей описать более или менее сложные структуры данных, все скрытые особенности JavaScript начинают проявляться. А если вам знакомы и вы приветствуете принципы объектно-ориентированного программирования, то вам и здесь захочется работать с данными в терминах объектов. JavaScript - язык очень простой и гибкий. Настолько простой, что его подход к организации работы с данными, процедурами и функциями можно назвать гениальным. Ну а гибкость как раз и является следствием этой простоты и гениальности. Итак, любая сложная структура данных в JavaScript, к коим относят знакомые всем структуры и объекты – это список пар “идентификатор-значение”, где идентификатор – это текстовый ключ, а значение может быть либо элементарным типом, либо вложенной структурой или объектом, либо функцией. Можно также назвать этот список ассоциативным массивом, хэш-таблицей, справочником и т.п. – деталей реализации я не знаю, да они могут и существенно отличаться в каждом отдельно взятом браузере. Далее и процедуры и функции я буду называть просто функциями, поскольку в JavaScript, как в прочем и во многих других современных языках программирования, они отличаются только типом возвращаемого результата (для процедур он просто не определен). А если учесть, что JavaScript – это язык с динамической типизацией, то внешне процедуры от функций вообще ничем не отличаются, за исключением наличия или отсутствия оператора return с аргументом. Вот несколько вариантов создания объекта на JavaScript:

   var obj1 = new Object();
   obj1.name = "тестовый объект 1";
   obj1.ToString = function() {alert(this.name)}; 

   var obj2 = {}
   obj2.name = "тестовый объект 2";
   obj2.ToString = function() {alert(this.name)}; 

   obj1.ToString();
   obj2.ToString();

Поскольку мы имеем дело с ассоциативным массивом членов объекта, то и работать с ним можно, как с обычным массивом, индексируемым строками. Следующий пример – полный аналог предыдущего:

   var obj1 = new Object();
   obj1["name"] = "тестовый объект 1";
   obj1["ToString"] = function() {alert(this.name)}; 

   var obj2 = {}
   obj2["name"] = "тестовый объект 2";
   obj2["ToString"] = function() {alert(this.name)}; 

   obj1.ToString();
   obj2["ToString"]();

Тот, кто знаком с объектами в других языках программирования может задать резонный вопрос: “А где тип объекта?” или “Куда делись классы?”. Ответ простой – классов в JavaScript нет. А нет их потому, что нет явного описания элементарных типов. Какой смысл создавать специальное описание класса, если его экземпляры – объекты легко смогут инициализировать свои свойства абсолютно разными типами благодаря динамической типизации. Если у вас возникла необходимость создать несколько объектов идентичной структуры, то вы можете описать функцию – фабрику объектов, примерно как в следующем примере:

   function CreateObject(name)
   {
    var obj = new Object();
    obj.name = name;
    obj.ToString = function() {alert(this.name)}; 
    
    return obj;
   }

   var obj1 = CreateObject("тестовый объект 1");
   var obj2 = CreateObject("тестовый объект 2");

   obj1.ToString();
   obj2["ToString"]();

Но есть еще более элегантный вариант. В контексте каждой функции в JavaScript, точно так же, как и в контексте любой структуры данных определен указатель this. Если вы вызываете функцию “просто так”, то this будет указывать на текущее окно. Если вы ее вызываете, как метод объекта, то this будет указывать на этот объект. Фокус следующего примера заключается в использовании оператора new вместе с вызовом функции. Ключевое слово new – это указатель на создаваемый объект, и поэтому this в контексте функции в этом случае будет указывать именно на этот объект. Таким образом, следующий пример – аналог конструктора объекта с параметрами на JavaScript. Также, говорят, что функция CreateObject и является тем самым классом объекта в JavaScript, а если точнее, то конструктором экземпляров этого класса:

   function CreateObject(name)
   {
    this.name = name;
    this.ToString = function() {alert(this.name)}; 
   }

   var obj = new CreateObject("Объект, созданный с использованием конструктора с параметрами");
   obj.ToString();

С вопросом описания и создания объектов на JavaScript пока все.

Прототипы или реализация наследования на JavaScript

Казалось бы, если нет классов, то о каком наследовании может идти речь? Ответ напрашивается сам собой – о наследовании самих объектов. Для любого созданного объекта на JavaScript можно выбрать базовый объект или прототип, определив свойство prototype. Таким образом, JavaScript – это прототипно-ориентированный язык программирования. Признаюсь, сам я наследование объектов в случае с JavaScript применяю редко, но считаю, что знать о такой возможности все-таки нужно. Пример:

   //Конструктор базового объекта
   function CreateBaseObject(name)
   {
    this.name = name;
    this.ToString = function() {alert(this.name)}; 
   }
   //Экземпляр базового объекта
   var base = new CreateBaseObject("Базовый объект");
   base.ToString();

   //Конструктор производного объекта
   function CreateInheritedObject(name)
   {
    this.name = "Производный объект: "+name; 
   }

   //Всем объектам, созданным с использованием функции CreateInheritedObject определяем единый прототип - экземпляр базового объекта. 
   CreateInheritedObject.prototype = base;
   
   var inherited = new CreateInheritedObject("Объект, созданный с использованием конструктора с параметрами");
   inherited.ToString();

Поскольку у нас наследование не классов, а наследование объектов, минуя эти самые классы, то экземпляр базового объекта обязан существовать. Свойство prototype по умолчанию определено всегда и указывает на пустой объект Object, который на самом деле не совсем и пустой. Object, например, содержит определение функции toString(), которое возвращает тип объекта в текстовом виде. Функция toString() класса Object и функция ToString(), описанная в примере – две абсолютно разные функции. Если функцию – конструктор объекта мы рассматриваем, как аналог определения класса в других объектно-ориентированных языках, то свойство prototype можно рассматривать, как аналог свойства base в том же контексте. Таким образом, через свойство prototype можно добавлять свойства и методы, разделяемые между всеми производными объектами текущей иерархии. Глядя не следующий код, я надеюсь, станет понятно, о чем идет речь.

   //Конструктор базового объекта
   function CreateBaseObject(name)
   {
    this.name = name;
   }

   CreateBaseObject.prototype.ToString = function() {alert(this.toString() + ": " + this.name)}; 

   //Экземпляр базового объекта
   var base = new CreateBaseObject("Базовый объект");
   base.ToString();

   //Конструктор производного объекта
   function CreateInheritedObject(name)
   {
    this.name = "Производный объект: "+name; 
   }
   //Всем объектам, созданным с использованием функции CreateInheritedObject определяем единый прототип - экземпляр базового объекта. 
   CreateInheritedObject.prototype = base;
   
   var inherited = new CreateInheritedObject("Объект, созданный с использованием конструктора с параметрами");
   inherited.ToString();

Полагаю, вы догадываетесь, что подобная имитация наследования на основе прототипов куда более гибкая, хотя и менее привычная, чем наследование на основе классов. Ведь структуру базового объекта мы описали один раз, а экземпляров его можем понаделать столько, сколько угодно, и различным производным объектам установить разные прототипы. Логика выбора реализации функции-метода также проста: сначала вызываемый метод ищем в производном объекте, и если не находим, то ищем его в объекте – прототипе и так далее вверх по цепочке. В данном примере функция ToString() определена только для базового класса, и только благодаря тому, что указан прототип функции CreateInheritedObject, вызов этой функции для объекта inherited стал возможным.

Реализация наследования на JavaScript без прототипов

Если желания связываться с прототипами нет, а определить класс, который бы наследовал все свойства и методы другого класса необходимо, то можно воспользоваться техникой определения и вызова конструктора базового класса. Для этого в конструкторе производного класса определим свойство для хранения ссылки на функцию создания базового класса bctor и вызовем ее с соответствующими параметрами. Пример приведен ниже.

   function CreateBaseObject(name)
   {
    this.name = name;
    this.ToString = function() {alert("Базовый объект: " + this.name)};    
   }

   function CreateInheritedObject(name)
   {
    //Определяем базовый конструктор
    this.bctor=CreateBaseObject;
    //Вызываем базовый конструктор
    this.bctor(name);

   //Переопределяем базовую функцию toString();
    this.ToString = function() {alert("Производный объект: " + this.name)};    
   }

   var base = new CreateBaseObject("объект 1");
   base.ToString();

   var inherited = new CreateInheritedObject("объект 2");
   inherited.ToString();

Казалось бы, почему просто не вызвать из конструктора производного класса функцию создания базового класса? Замена строк

    this.bctor=CreateBaseObject;
    this.bctor(name);

на вызов

   CreateBaseObject(name);

не приведет к желаемому результату, поскольку в таком случае this в контексте CreateBaseObject() будет указывать не на создаваемый объект, а на окно браузера, и производный класс не получит определения свойства name.

Массивы на JavaScript

Если с помощью Object в JavaScript мы создаем так называемые ассоциативные массивы (индексация элементов по текстовому ключу), то для создания массивов элементов, индексируемых целочисленными значениями следует использовать объект Array. Массивы в JavaScript, что не удивительно, практически не признают никаких ограничений: их размер может быть изменен в процессе выполнения программы, а элементы одного массива могут быть разного типа. Тем не менее, в JavaScript нет явного механизма для создания многомерных массивов (двух, трехмерных массивов), поэтому приходится использовать достаточно гибкий механизм определения массива массивов. Объект Array можно создавать явно и неявно. Если при создании Array в качестве аргументов передается лишь один параметр, то он интерпретируется не как значение его единственного элемента, а как первоначальный размер массива пустых элементов. Впрочем, варианты создания массивов и пример их обработки (рекурсивная функция listArray()) представлен ниже, поэтому, что-либо писать еще про массивы в JavaScript не вижу смысла.

//Пример 1. Создание линейного массива из 3-х элементов
var a1 = [1,2,3];
alert(listArray(a1));
//Результат: [1,2,3]

//Пример 2. Создание линейного массива с помощью явного создания экземлпяра Array
var a2 = new Array(1,2,3);
alert(listArray(a2));
//Результат: [1,2,3]

//Пример 3. Создание пустого массива
var a3=[];
//Добавление элементов в массив
a3[0]=1;
a3[1]=2;
a3[2]=3;
alert(listArray(a3));
//Результат: [1,2,3]

//Пример 4. Создание массива определенной длины с пустыми значениями
var a4=new Array(3);
alert(listArray(a4));
//Результат: [null,null,null]

//Пример 5. Ничто не мешает добавить элемент в конец массива, даже если его длина 
//          была определена в момент его создания.
var a5=new Array(3);
a5[3]=4;
alert(listArray(a5));
//Результат: [null,null,null,4]

//Пример 6. Создание двумерного массива, как массива массивов
var a6 = [[1,2,3],[11,12,13],[21,22,23]];
alert(listArray(a6));
//Результат: [[1,2,3],[11,12,13],[21,22,23]]

//Пример 7. Создание массива массивов произвольной размерности
var a7 = [[1,2,3],[11,[121,122,123],13],[21,22,23]];
alert(listArray(a7));
//Результат: [[1,2,3],[11,[121,122,123],13],[21,22,23]]

//Пример 8. Создание массива с элементами разных типов
var a8 = [1,"two",3,true,,false];
alert(listArray(a8));
//Результат: [1,[t][w][o],3,true,null,false]
//           "two" преобразовалось в [t][w][o], поскольку строка
//            тоже в некотором виде является массивом.

//Пример обработки массива: преобразование массива в строку
function listArray(a)
{
 if (a == null)
     return "null";

 if (a.length == undefined)
     return a.toString();

 if (a.length == 1)
     return "[" + a.toString() + "]";
 
 var result = "[";

 for (var i = 0; i < a.length; i++)
     {
       var s = (i < a.length - 1) ? "," : "";
       result = result + listArray(a[i]) + s;
     }
 
 result = result + "]";
 return result;
}

Думаю, что на этом рассказ об особенностях языка программирования JavaScript стоит закончить и продолжить его изучать на практике: на примерах приложений, представленных в этом и предыдущем разделах.

Читать дальше: Программирование на JavaScript с использованием библиотеки jQuery

Компьютерные курсы и курсы программирования
Основы программирования

Курс для начинающих программистов на C# и VB.NET.

SQL 25™

Построение SQL запросов и работа с базой данных.

C# Quick Guide™

Программирование на C#. Краткое руководство.

RegEx

Применение регулярных выражений.

Plug-in архитектура

Примеры программной Plug-in архитектуры.

XML и его расширения

Язык разметки XML и его расширения с примерами.

HTML и разметка гипертекста

Языки HTML, XHTML и CSS с примерами разметки.

Основы веб-дизайна

Основы веб-дизайна: решения типовых задач верстки.

Программирование на PHP

Руководство по программированию на PHP для начинающих.

Справочные материалы

Шаблоны проектирования
Каталог шаблонов проектирования программных компонентов.

Рефакторинг кода
Каталог приемов рефакторинга программного кода.

Гость
Имя

Пароль



Забыли пароль?
Запросите новый здесь
.
Coding Craft. Все права защищены © 2011. Проект Инициативного Народного Фронта Образования - ИНФО-проект.