Переменные Private, Public и Static



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

1. Скрытая ( Private ) - объявляется с помощью var variableName или function functionName внутри объекта. Могут быть доступны только другим скрытым или привилегированным функциям.

2. Открытая ( Public ) - объявляется с помощью this.variableName внутри объекта. Может изменяться любой функцией или методом.

3. Привилегированная ( Privileged ) - объявляется с помощью this.functionName = function(){ ... } внутри объекта. Доступна для любой функции или метода и может обращаться или изменять любую скрытую переменную.

4. Прототипированная ( Prototype ) - объявляется с помощью Class.prototype.variableName или Class.prototype.functionName. Объявленные таким образом функции будут иметь доступ к любой открытой или прототипированной функции. Попытки изменить созданную таким образом переменную будут вместо этого создавать новую открытую переменную на объекте, а прототипированная переменная будет недоступна.

5. Статическая ( Static ) - объявляется с помощью Class.variableName или Class.functionName. Может изменяться любой функцией или методом. Такой метод используется редко.

Чтобы понять различия между уровнями, давайте рассмотрим пример:

function Cat(name, color){ /* Конструктор: при создании объекта выполняется любой находящийся здесь код   */ Cat.cats++; /* Скрытые переменные и функции доступны только скрытым или привилегированным   функциям. Отметим, что 'name' и 'color', переданные в Class, уже являются   скрытыми переменными. */ var age = 0; var legs = 4; function growOlder(){ age++; } /* Открытые переменные доступны открыто или скрыто   */ this.weight = 1; this.length = 5; /* Привилегированные функции доступны открыто или скрыто.   Могут обращаться к скрытым переменным. Невозможно изменить, можно только заменить открытой версией */ this.age = function(){ if(age==0) this.length+=20; growOlder(); this.weight++; }} /*Прототипированные функции доступны открыто */Cat.prototype = { talk: function(){ alert('Meow!'); }, callOver: function(){ alert(this.name+' ignores you'); }, pet: function(){ alert('Pet!'); }} /*Прототипированные переменные доступны открыто.Нельзя перезаписать, только заменить открытой версией */ Cat.prototype.species = 'Cat'; /*Статические переменные и функции доступны открыто */ Cat.cats = 0;

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

1.9. Наследование и замыкание.

Наследование

В "Основы объектно-ориентированного программирования" была создана функция 'Cat':

function Cat(name){ this.name = name;}Cat.prototype = { species: 'Cat', talk: function(){ alert('Meow!'); }, callOver: function(){ alert(this.name+' ignores you'); }, pet: function(){ alert('Pet!'); }}

Теперь можно создать любое количество котов, но как быть, если мы захотим создать объект другого типа, например, собаку? В этом случае понадобится создать совершенно новую функцию, со своими собственными прототипами. Если два объекта используют одни и те же функции (например, можно было бы добавить функции sleep (спать), eat (есть), и play (играть)), то в результате мы бы имели чрезмерное дублирование кода. Решением является концепция наследования.

По сути наследование позволяет определить объекты "предки" и "потомки". Потомок наследует все свойства своего предка. Можно было создать, например, функцию Animal, Pet или Mammal. Обе функции Cat и Dog обладали бы многими свойствами функции предка Animal, и нам пришлось бы писать этот код один раз.

Проблема в том, что JavaScript не имеет в действительности встроенного механизма наследования, поэтому эту функциональность необходимо создавать самостоятельно. Для этого существует несколько различных способов. Один из них состоит в использовании функции call. Эта функция позволяет вызывать одну функцию из контекста другой, т.е. мы можем определить, как действует ключевое слово this. С помощью call можно написать класс Animal (Животное), а затем вызвать его из класса Cat или Dog.

function Animal(name){ this.name = name; this.species = 'Animal'; this.sleep = function(){ alert(this.name+' спит: Хрррр'); }}function Cat(name){ Animal.call(this, name); this.talk = function(){ alert('Мяу!'); }}function Dog(name){ Animal.call(this, name); this.talk = function(){ alert('Гав!'); }} var sam = new Cat('Sam');var joe = new Dog('Joe');sam.sleep(); // Sam спит: Хррррjoe.sleep(); // Joe спит: Хрррр sam.talk(); // Мяу!joe.talk(); // Гав!

Хотя это работает, мы немного ограничены в своих возможностях. Например, прототипирование не действует при использовании этого метода: все прототипы, заданные на Animal, не будут переноситься в функции Cat или Dog. Как мы знаем из "Основы объектно-ориентированного программирования" , определенные внутренне с помощью " this." функции создают новый экземпляр всякий раз при создании новой копии предка. В этом случае всякий раз при создании функции Animal, Cat или Dog появляется новая копия функций species и sleep. Как можно догадаться, это не самый эффективный способ.

Лучшим подходом является прототипирование всего родительского класса на классе-потомке. Это предоставляет доступ ко всем свойствам и методам класса предка:

function Animal(name){ this.name = name;}Animal.prototype = { species: 'Animal', sleep : function(){ alert(this.name+' спит: Хрррр'); }} function Cat(name){ Animal.apply(this, arguments);}Cat.prototype    = new Animal;Cat.prototype.species = 'Cat';Cat.prototype.talk = function(){ alert('Мяу!'); } function Dog(name){ Animal.apply(this, arguments);}Dog.prototype    = new Animal;Dog.prototype.talk = function(){ alert('Гав!'); } var sam = new Cat('Sam');var joe = new Dog('Joe'); sam.sleep(); // Sam спит : Хррррjoe.sleep(); // Joe спит: Хрррр alert(sam.species); // Catalert(joe.species); // Animal - для Dog функция species не определена

Можно продолжить это дальше и создать отдельные функции для различных пород собак или кошек и т.д.

Замыкание

Замыкание ( сlosure ) является одним из наиболее мощных средств JavaScript. Если воспользоваться простым объяснением, то замыкание связывает внутренние и внешние переменные в функции. Почему это так важно? Потому что замыкание позволяет эмулировать почти любое свойство любого языка программирования, даже если оно не существует в JavaScript. Это звучит немного непонятно, поэтому лучше начать с более простого примера:

function beginAdding(a){ a *= 5; return function finishAdding(b){ alert(a+b); }} var add = beginAdding(10); add(20); // 70

Можно видеть, что в приведенном коде переменной 'a' присваивается значение 10 и передается в функцию beginAdding, в то время как переменной 'b' присваивается значение 20 и передается в функцию finishAdding.

А что содержится в переменной 'add'? Она содержит функцию finishAdding с копией связанной с ней всей функции beginAdding. Копия переменной 'a' из beginAdding сохраняется в памяти для дальнейшего использования.

Теперь, имея представление о замыкании, прежде чем продолжать, необходимо обсудить проблему утечки памяти. Internet Explorer, в частности, подвержен достаточно неприятным утечкам памяти при использовании замыкания. Чтобы понять, почему необходимо знать о сборке мусора в Internet Explorer, посмотрим, как в этом браузере выполняется очистка памяти от ненужных объектов.

Internet Explorer имеет два отдельных сборщика мусора: один для JavaScript и другой для объектов DOM. При выгрузке страницы браузер просматривает и удаляет весь код JavaScript и все объекты DOM со страницы. Утечка происходит, когда имеются циклические ссылки из объекта DOM в JavaScript и снова на объект DOM или из JavaScript-->Dom-->Javascript. Internet Explorerзапутывается и не удаляет объекты при циклической ссылке.

var someInput = document.getElementById('inputbox');var someFunction = function(){ alert(someInput.value);} someInput.onclick = someFunction;

Здесь представлен бесконечный цикл в терминах замыканий. Объект DOM someInput вовлечен в замыкание с функцией someFunction и наоборот. Браузер не может определить, что удалить в первую очередь, поэтому в результате имеем утечку памяти.

Замыкания создают, часто даже не осознавая этого. Возьмем, например, простую функцию:

function Animal(name){ this.sleep = function(){ alert(name+' спит: Хрррр'); }}

Можно не заметить этого, но переменная 'name' в функции sleep приходит из родительской функции Animal. Это создает замыкание.

Даже определение простейшей функции может создавать замыкание:

var x = 5;var n = function(){ x=10; return x;}

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

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

var x  = 5;var alertX1 = function(){ alert(x); } x      = 10;var alertX2 = function(){ alert(x); } alertX1();alertX2();

Обе функции в этом случае выведут 10. Почему? Потому что они обе указывают на одну и ту же копию х.

 

Если изменить x в любой точке, то обе функции отразят это. Как это исправить? Проще всего изменить область действия замыкания:

function makeClosure(x){ return function(){ alert(x); }} var x  = 5;var alertX1 = makeClosure(x); x      = 10;var alertX2 = makeClosure(x); alertX1(); // 5alertX2(); // 10

Это решает проблему. Если, однако, этот код создавал утечку памяти, то утечка будет существенно больше, чем в предыдущем примере; и также используется больший объем памяти. Оказывается, что в действительности имеется три различных области действия в этом простом примере:

 

Можно видеть, что переменная х копируется в каждую из двух областей действия. Это связано с тем, что x является строкой или числом. JavaScript всегда передает строки и числа по значению - то есть всегда делается копия переменной. С объектами все происходит иначе. Если х является функцией, массивом или базовым объектом, то в этом случае ссылка в двух наших функциях происходит на одну и ту же копию xи поэтому в результате выводимое сообщение будет одинаковым:

function makeClosure(x){ return function(){ alert(x.val); }} var x  = {val:5};var alertX1 = makeClosure(x); x.val  = 10;var alertX2 = makeClosure(x); alertX1(); // 10alertX2(); // 10

Отличие состоит в том, что x теперь является объектом. Каждая из дополнительных областей действия указывает на одну и ту же копию x. При изменении значения x оно изменяется в каждой области действия, в которой х упоминается.

Как избежать утечки памяти при использовании замыканий? Необходимо избегать использования циклических ссылок. Наиболее распространенной причиной утечки памяти является присоединение событий, таких, как событие onclick, к объектам DOM.

Часто совершенно невинный с виду код будет создавать утечку и ее бывает крайне сложно обнаружить. К счастью, протестировать наличие утечки памяти достаточно легко. Если при каждом обновлении страницы используемая память увеличивается, то приложение имеет утечку. Отследить, где это происходит, совершенно другая задача, но по крайней мере теперь известно о наличии проблемы!

1.10. Основы приложений AJAX.

В последнее время термин AJAX получил широкое распространение. По сути, это необычное название технологии, которая уже давно существует. Однако за последний год JavaScript в стиле AJAX стал очень популярным у многих разработчиков, и мы начинаем видеть, как с его помощью стали создавать различные интересные вещи. Google Maps и GMail являются двумя наиболее широко известными приложениями AJAX, но в последнее время и другие компании по всему миру начали использовать ее на своих сайтах.

AJAX означает " Asynchronous JavaScript and XML ", т.е. Асинхронный JavaScript и XML. В действительности AJAX состоит из JavaScript, как и любое другое приложение, и соединения XMLHTTP с Web-сервером. Вот и все! Общая идея заключается в том, что можно извлекать с сервера данные просто по мере необходимости, не обновляя всю страницу.

Прежде всего AJAX почти всегда опирается на серверный язык, такой, как PHP или ASP. Когда пользователю необходимо получить новые данные, JavaScript запрашивает их, а сервер, вероятно, запросит базу данных и затем вернет данные. Эти данные можно вернуть в различной форме. Если они структурированы, то это будут обычно данные XML или JSON. Если это очень простые данные (такие, как получение описания какого-то объекта), то можно часто увидеть людей, которые просто записывают эти данные непосредственно в ответ AJAX.

Создание объекта XMLHttp

При создании запроса AJAX прежде всего необходимо создать объект XMLHTTP. Netscape/Firefox, Opera и другие браузеры имеют этот объект встроенным. Internet Explorer использует ActiveXObject. Поэтому мы создадим одну функцию для работы со всеми этими браузерами:

if(typeof(XMLHttpRequest)!='undefined'){ var getXMLHttpObj = function(){ return new XMLHttpRequest(); }} else { var getXMLHttpObj = function(){ var activeXObjects = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP']; for(var i=0; i<activeXObjects.length; i++){ try{   return new ActiveXObject(activeXObjects[i]); }catch(err){} } }}

Любой браузер, который поддерживает объект XMLHttpRequest, будет использовать этот встроенный объект. Internet Explorer 6 и более ранние версии будут использовать объект ActiveX XMLHttp компании Microsoft. Существует много различных версий этого объекта, поэтому мы попробуем их все, начиная с самого нового.

Теперь, когда мы имеем объект XMLHttp, можно разместить запрос на сервере:

var oXml = getXMLHttpObj();oXml.open('GET', 'getData.php', true);oXml.onreadystatechange = processingFunction;oXml.send(); function processingFunction(){ if(oXml.readyState!=4) return; // запрос не выполнен // Результаты обрабатываются здесь. Подробнее дальше!}

После создания объекта XMLHttp остается еще 4 дополнительных шага. Сначала определяются параметры соединения с помощью функции .open. Функция .open получает 3 аргумента: тип запроса, URL и флаг, который сообщает объекту, выполняться или нет асинхронно.

Первый аргумент, тип запроса, почти всегда будет GET или POST. Если извлекаются данные, то это будет обычно GET, а если отправляется форма с AJAX, то это часто будет POST.

Флаг асинхронности немного отличается почти от всего остального в JavaScript. Если этот флаг задан как false, то код продолжает выполняться, как и любой другой фрагмент кода, то есть он ожидает, пока объект XMLHttp завершит свою работу, прежде чем двигаться дальше. Однако, если этот флаг задан как true, то код продолжает делать то, что следует в коде после запроса. Когда состояние запроса изменяется, вызывается функция, которая определена в onreadystatechange.

В чем различие? Если флаг асинхронности задан как false, то браузер будет полностью заблокирован, пока обрабатывается запрос. Если запрошенные данные нужны в обязательном порядке для продолжения работы, то используйте это значение флага. Если флаг задан как true, то пользователь может продолжать использовать Web-страницу, но ничего не известно о том, когда вернется ответ на запрос. Может потребоваться 1/2 секунды или 1 минута. Все зависит от нагрузки на сервер и соединения конечного пользователя.

Когда все готово, посылается запрос. Затем мы ожидаем. Если запрос выполняется синхронно с функцией обработки, определенной с помощью onreadystatechange, то она будет вызываться несколько раз. На самом деле она вызывается всякий раз при изменении состояния запроса. Существуют различные состояния, такие, как отправка и загрузка данных, но нас интересует только завершение загрузки данных. Если readyState == 4, то запрос выполнен, поэтому в любом другом случае мы просто выходим из функции.

Теперь мы получили данные, но что это за данные?

XML, JSON или текст

Существует три возможных варианта получения данных: XML, JSON или обычный текст. При извлечении данных из базы данных, скорее всего, будет использоваться XML или JSON. Ни один из этих вариантов не имеет явных преимуществ. XML - широко распространенный стандарт, и поэтому существует много приложений, которые работают с файлами XML. JSON является более новой идеей, но быстро становится популярным. Его обычно легче прочитать (для человека), и он требует немного меньшую полосу пропускания для пересылки.

Предположим, что создается приложение для управления контактами. Cервер может возвращать информацию о людях. Одни и те же данные можно выразить в форме XML или JSON:

XML:

<xml> <contacts> <person firstname="Joe" lastname="Smith" phone="555-1212" /> <person firstname="Sam" lastname="Stevens" phone="123-4567" /> </contacts></xml>

JSON:

{contacts:[ {"firstname":"Joe", "lastname":"Smith", "phone":"555-1212"}, {"firstname":"Sam", "lastname":"Stevens", "phone":"123-4567"}]}

Можно видеть, что нотация XML выглядит очень похоже на HTML. По большей части это так и есть. HTML и XML оба являются основанными на тегах языками и могут даже анализироваться одинаковым образом (см. "Лекция 6" ).

Нотация JSON выглядит очень похоже на простой JavaScript. JSON означает JavaScript Object Notation и поэтому действительно является обычным JavaScript.

Данные в любой нотации можно посылать с Web-сервера просто как обычный текст. Никакие пробелы, имеющиеся в этих примерах, не нужны, за исключением одиночных пробелов между именами атрибутов в тегах person (в версии XML ).

Формат XML является совокупностью тегов, очень похожих на HTML. Можно иметь любое количество тегов, вложенных друг в друга, и каждый тег может иметь любое количество атрибутов: например, firstname, lastname и phone в примере выше. Однако имеется несколько вещей, за которыми необходимо следить.

· Каждый тег должен иметь закрывающий тег. Например, <contacts> закрывается с помощью расположенного ниже </contacts>. Теги person являются замкнутыми. /> в конце действует по сути как дополнительный закрывающий тег. Можно было так же легко написать один из них как <person... ></person>.

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

& --> &amp;
< --> &lt;
> --> &gt;
" --> &quot;
' --> &#39;

· Например, узел <group name="Bill & Paul" /> является недопустимым и должен быть заменен на <group name="Bill &amp; Paul" />.

· Приходящие с сервера данные должны посылаться с content-type, заданным как text/xml. Если извлекается файл с расширением .xml, то это должно происходить автоматически. Если данные извлекаются из сценария, необходимо задавать это вручную.

Для PHP добавьте следующее:

<?php header('Content-type: text/xml'); ?>

Для ASP добавьте:

<% response.contentType = "text/xml" %>

· Для всех других языков добавьте эквивалентный заголовок content-type.

Если этот заголовок не задан, свойство responseXML объекта XMLHttp будет пустым (это свойство будет описано далее).

JSON имеет аналогичный набор правил, и всю документацию по способам записи можно увидеть на http://www.json.org/. Однако упрощенно можно сказать, что:

· объекты начинаются и заканчиваются с помощью символов { и } соответственно;

· массивы начинаются и заканчиваются с помощью символов [ и ] соответственно;

· все строки заключаются в двойные кавычки " ;

· символы " в строке должны экранироваться: \".

Проще говоря, строка JSON должна представлять допустимый объект JavaScript.

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

function processingFunction(){ if(oXml.readyState!=4) return; // запрос не выполнен // Результаты обрабатываются здесь. Подробнее дальше!}

Когда скрипт попадает в тело функции, запрос XMLHttp будет выполнен. Объект XMLHttp имеет два метода для возврата данных: responseXML и responseText. Так как в данный момент мы работаем с файлом XML, то будем использовать responseXML:

function processingFunction(){ if(oXml.readyState!=4) return; var xmlDoc = oXml.responseXML; var contacts = xmlDoc.selectNodes('/xml/contacts/person'); alert('There are '+contacts.length+' contacts!'); for(var i=0; i<contacts.length; i++){ alert('Contact #'+(i+1)+':\n\n'+   'First Name: '+contacts[i].getAttribute('firstname')+'\n'+   'Last Name: '+contacts[i].getAttribute('lastname') +'\n'+   'Phone #: '+contacts[i].getAttribute('phone') +'\n'); }}

Здесь имеется 3 функции вывода ( alert ). Одна сообщает, что имеется два контакта, а еще две выводят контактную информацию для каждого человека.

Посмотрим на тот же сценарий, использующий текст JSON:

function processingFunction(){ if(oXml.readyState!=4) return; var json = eval('('+oXml.responseText+')'); alert('There are '+json.contacts.length+' contacts!'); for(var i=0; i<json.contacts.length; i++){ alert('Contact #'+(i+1)+':\n\n'+ 'First Name: '+json.contacts[i].firstname+'\n'+ 'Last Name: '+json.contacts[i].lastname +'\n'+ 'Phone #: '+json.contacts[i].phone +'\n'); }}

Как можно видеть, строки JSON можно преобразовать в JavaScript, используя просто встроенную в JavaScript команду eval(). Однако это можно делать, только если вы полностью доверяете источнику данных. Если это не так (если данные поступают из не вполне известного источника данных), то необходимо пропустить их в целях безопасности через JSON Parser (Анализатор JSON ).

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

Что использовать: XML или JSON? Здесь нет больших различий. XML имеет более широко распространенный формат и переносим почти на любую систему. Если создаваемый проект будет работать с внешними источниками, то, вероятно, лучше использовать XML. Однако JSONнемного легче для понимания и в общем он быстрее для разработки кода, чем XML. Если эта технология применяется для персонального проекта или в начале нового проекта, которому не нужно взаимодействовать с другими приложениям, то JSON определенно заслуживает рассмотрения.

Пример со списком контактов

В действительности уже есть все, что нужно для создания приложений AJAX, но мы рассмотрим достаточно простой пример. Мы собираемся написать небольшую таблицу данных ( data grid ), которая извлекает данные из трех различных файлов JSON. Для простоты эти файлы уже были сгенерированы. На практике эти файлы будут скорее всего генерироваться оперативно с помощью серверного сценария.

Файл 1

{contacts:[ {"firstname":"Steve" ,"lastname":"Smith", "phone":"555-1212"},   {"firstname":"Joe" ,"lastname":"Stevens", "phone":"555-0193"},   {"firstname":"Sam" ,"lastname":"Smith", "phone":"555-5120"},   {"firstname":"Dave" ,"lastname":"Stevens", "phone":"555-0521"},   {"firstname":"Suzy" ,"lastname":"Smith", "phone":"555-9410"},   {"firstname":"Jessica" ,"lastname":"Stevens", "phone":"555-8521"},   {"firstname":"James" ,"lastname":"Smith", "phone":"555-4781"},   {"firstname":"Jacob" ,"lastname":"Stevens", "phone":"555-9281"},   {"firstname":"Alex" ,"lastname":"Smith", "phone":"555-7261"},   {"firstname":"Tam" ,"lastname":"Stevens", "phone":"555-1820"}]}

Файл 2

{contacts:[ {"firstname":"Nancy" ,"lastname":"Smith", "phone":"555-9583"},   {"firstname":"Elane" ,"lastname":"Stevens", "phone":"555-7281"},   {"firstname":"Shawn" ,"lastname":"Smith", "phone":"555-5782"},   {"firstname":"Jessie" ,"lastname":"Stevens", "phone":"555-7312"},   {"firstname":"Matt" ,"lastname":"Smith", "phone":"555-4928"},   {"firstname":"Jason" ,"lastname":"Stevens", "phone":"555-3917"},   {"firstname":"Daniel" ,"lastname":"Smith", "phone":"555-8711"},   {"firstname":"Shannon" ,"lastname":"Stevens", "phone":"555-0912"},   {"firstname":"Diana" ,"lastname":"Smith", "phone":"555-6172"}, {"firstname":"Mark" ,"lastname":"Stevens", "phone":"555-8831"}]}

Файл 3

{contacts:[ {"firstname":"Laura" ,"lastname":"Stevens", "phone":"555-3915"},   {"firstname":"Jeff" ,"lastname":"Smith", "phone":"555-8614"},   {"firstname":"Frank" ,"lastname":"Stevens", "phone":"555-0213"},   {"firstname":"Elizabeth" ,"lastname":"Smith", "phone":"555-7531"},   {"firstname":"Jim"  ,"lastname":"Stevens", "phone":"555-3951"}]}

Эти файлы будут обеспечивать все данные для нашего списка контактов на AJAX. Построение списка контактов является в действительности вполне простым: создается таблица TABLE для хранения всех контактов и функция для очищения и повторного заполнения этой таблицы. Вот и все.

<table cellspacing="1" cellpadding="3" bgcolor="#000000" style="font-family:tahoma;font-size:10px;"> <tbody id="contactListTable"> <tr style="background-color:#CCF;"> <th>First Name</th> <th>Last Name</th> <th>Phone #</th> </tr> </tbody></table> function loadContactListPage(n){ var oXML = getXMLHttpObj(); oXML.open('GET', '/img/10_json_file'+n+'.txt', true); oXML.onreadystatechange = function(){ doneLoading(oXML); } oXML.send('');} function doneLoading(oXML){ if(oXML.readyState!=4) return; var json = eval('('+oXML.responseText+')'); var table = document.getElementById('contactListTable'); for(var i=table.childNodes.length-1; i>0; i--){ table.removeChild(table.childNodes[i]); } for(var i=0; i<json.contacts.length; i++){ var tr = document.createElement('TR'); var td1 = document.createElement('TD'); var td2 = document.createElement('TD');    var td3 = document.createElement('TD'); tr.style.backgroundColor = i%2?'#FFF':'#E6E6E6'; table.appendChild(tr); tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3); td1.appendChild(document.createTextNode(json.contacts[i].firstname)); td2.appendChild(document.createTextNode(json.contacts[i].lastname)); td3.appendChild(document.createTextNode(json.contacts[i].phone)); }}

Демонстрационный пример

First Name Last Name Phone # Steve Smith 555-1212Joe    Stevens 555-0193Sam Smith 555-5120Dave Stevens 555-0521Suzy Smith 555-9410Jessica Stevens 555-8521James Smith 555-4781Jacob Stevens 555-9281Alex Smith 555-7261Tam Stevens 555-1820 Page 1 | Page 2 | Page 3

Как можно видеть из примера выше, это все достаточно просто. Большая часть кода нужна в действительности просто для создания новых строк в таблице.

AJAX может быть удивительно полезным инструментом. Его можно использовать для проверки форм перед их отправкой, для извлечения данных, как в этом примере, или для чего-то еще, что можно будет придумать. Однако в нормальной ситуации он не должен быть основным элементом Web-сайта. Обычно надо быть уверенным, что сайт будет доступен, даже если JavaScript будет отключен, но всегда существуют некоторые исключения для этого правила.

1.11. Обработка ошибок в JavaScript.

Синтаксические ошибки

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

for(var i=0; i<10; i++) // рабочий код здесь }

Обратите внимание, что здесь пропущена открывающая скобка {. Если попробовать выполнить этот код, то немедленно появится сообщение об ошибке, даже если код находится в функции, которая не выполняется сразу. Такие ошибки почти всегда легко находятся и исправляются. В этом случае будет получено сообщение, говорящее что-нибудь подобное " Ожидалась ')', строка 10, позиция 18 ". Если перейти к строке 10, то там обычно будет находиться достаточно очевидная ошибка, такая, как пропущенная ), дополнительный знак < или какая-то другая опечатка. С такими ошибками ничего нельзя сделать, кроме как просто исправить и двигаться дальше. Ниже представлен список некоторых наиболее распространенных синтаксических ошибок:

· Пропущенные или непарные фигурные, круглые или квадратные скобки

Каждая фигурная {, круглая (, или квадратная [ скобка должна иметь свою закрывающую парную скобку. Если имеются вложенные скобки, то внутренние должны быть закрыты прежде, чем внешние. Например, набор скобок {[}] является недопустимым.

Условия операторов if, for и while должны помещаться в круглые скобки. Выражнение " if x=5{ " является недопустимым, так как " x=5 " должно быть заключено в круглые скобки. Если с этим возникнут проблемы, то существуют редакторы, такие, как EditPlus, которые могут выделять соответствующие пары скобок, и т.д.

· Пропущенные или непарные кавычки

Это очень распространенная проблема. Строки в JavaScript начинаются символом ' или " и должны заканчиваться таким же символом. Если этот символ существует в строке, то он должен быть экранирован. Например, код

var x = 'It's a beautiful day';

является недопустимым, потому что ' в It's не экранировано. Этот код должен выглядеть следующим образом:

var x = 'It\'s a beautiful day'; // или var x = "It's a beautiful day";

Еще одной достаточно распространенной ошибкой является завершение строки другим символом, т.е.:

var x = "It's a beautiful day';

Эта строка начинается с символа ", поэтому должна закончиться также символом ".

· Пропущенная точка с запятой

Хотя точки с запятой обычно не нужны в JavaScript, но лучше все же их использовать. Например, если нужно сократить файл JavaScript, то обычно удаляют все переносы строк. Возьмем следующий код:

var x=5var y=10

Если удалить переносы строк, то получим код

var x=5 var y=10

который вызовет ошибку. Если бы использовались точки с запятой, то проблемы не было бы.

Ошибки времени выполнения

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

alert(x); // 'x' не определено var x; x[5] = 'test'; // 'x' будет null или не является объектом window.frames = 5; // Не реализовано var for; // ожидается идентификатор document.doesNotExist(5);// объект не поддерживает это свойство или метод alert(parseInt('5')); // ожидается объект

Многие из этих проблем вызываются более общими ошибками, которые приходится разыскивать.

· Неправильное использование прописных букв

Все встроенные функции JavaScript используют специальную форму записи имен функций, предполагающую, что имя функции начинается со строчной буквы, а в начале каждого следующего слова будет использоваться прописная буква: parseInt, getElementById, createElement, appendChild, и т.д.

Так как JavaScript учитывает регистр символов, то неправильный ввод имени одной из этих функций часто будет приводить к ошибке во время выполнения.

· Ссылка на несуществующий код, функции или объекты DOM

Эта проблема возникает обычно в отношении объектов DOM. Предположим, что имеется код, который изменяет некоторые элементы формы на странице. Если делается попытка выполнить этот код до появления элементов формы, например, если поместить его в тег <HEAD>, то будет получена ошибка JavaScript.

Обычно эта проблема легко решается. Лучшим решением будет выполнение кода по событию onload, например:

<BODY onload="loadFunction();">

или еще лучше, присоединение события к загрузке тела:

document.captureEvents(Event.LOAD);document.onLoad=loadFunction;

· Использование зарезервированного слова

Существует длинный список зарезервированных ключевых слов JavaScript. Если делается попытка использовать многие из них вне их специального контекста, как, например, запись

var for = 5;

то будет возникать ошибка.

· Использование пропущенного параметра

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

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

Однако последний тип ошибки из этого списка с пропущенными параметрами можно проверить достаточно легко:

function myFunction(a, b, c){ if(a){ // выполняется работа с a } if(b && c){ // выполняется работа с b и c }}

Если функция вызывается только с одной переменной, то проблемы не возникает. Однако надо помнить об одной вещи: если входящая по значению переменная может быть определена как false (0 или false ), то код не будет работать. В связи с этим лучше проверять, что переменная не была определена:

function myFunction(a, b, c){ if(typeof(a)!='undefined'){ // выполнение кода с a } if((typeof(b)!='undefined') && (typeof(c)!='undefined')){ // выполнение кода с b и c }}

В этом случае, даже если одна из переменных будет передана как 0, false или null, код все равно будет работать.

Сейчас мы перейдем к изучению механизмов обработок ошибок - с помощью операторов Try/Catch и функции window.onerror.

Window.onerror

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

window.onerror = function(){ return true;}

С этим кодом сообщение об ошибке никогда не будет выводиться. Однако в связи с этим приложение может не работать. Например, если бы переменная была null и с ней была выполнена какая-то операция, то в обычной ситуации должно появиться сообщение об ошибке. При использовании этого кода функция или сценарий в случае возникновения ошибки будет просто молча останавливаться.

Функцию window.onerror можно также использовать для вывода пользователям несколько более дружественных сообщений об ошибках. Можно просто вывести, например, сообщение 'Произошла ошибка, свяжитесь, пожалуйста, с Web-мастером', вместо вывода пользователю всех технических деталей ошибки (что большинство браузеров делает по умолчанию).

Еще одно использование window.onerror состоит в отправке разработчику списка всех ошибок, произошедших на сайте. Можно использовать AJAX для отправки сообщений об ошибках в форме, чтобы можно было позже их исправить. Все это возможно сделать неявно, без взаимодействия с пользователем.

Try/Catch/Finally и Throw

Операторы Try/Catch являются несомненно наиболее распространенным и обычно лучшим способом реализовать обработку ошибок в JavaScript. Но не только это - операторы Try/Catch могут иногда быть единственным способом реализовать некоторые задачи, такие, как обнаружение объекта. Возьмем, например, простую функцию для создания в Internet Explorer объекта XMLHttp:

var activeXObjects = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0','Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP'];for(var i=0; i<activeXObjects.length; i++){ try{ return new ActiveXObject(activeXObjects[i]); }catch(err){}}

Заранее неизвестно, какие объекты установил пользователь, и, к сожалению, браузер не предоставляет никакого механизма это определить. Поэтому остается создавать каждый из 6 возможных объектов, пока один из них (будем надеяться) не заработает.

Операторы Try/Catch можно использовать для перехвата ошибок двух типов: ошибок времени выполнения и ошибок пользователя. Ошибки времени выполнения, как говорилось ранее, возникают, когда у компилятора JavaScript существует проблема с созданным кодом. Ошибкипользователя, с другой стороны, будут технически проходить без проблем, но возникают в связи с контекстом приложения. Если имеется поле, в которое пользователь, например, должен ввести свой возраст, и пользователь вводит -2, то это приводит к появлению ошибки.

Блок Try/Catch имеет достаточно простой синтаксис:

try{ // код }catch(err){ // код обработки ошибки }

Если код в блоке try приводит к ошибке, то сценарий немедленно переходит в блок catch. Объект ошибки err в JavaScript имеет ряд полезных свойств - описание, сообщение, имя и номер, которые можно использовать для вывода информации о том, что произошло:

try{ var x; x[5] = 5;}catch(err){ alert('An error occured: '+err.description);}

Если в операторе catch окажется ошибка, то JavaScript сможет обратиться в дальнейшем к ее описанию.

Такой блок кода Try/Catch можно применять в любом месте. Однако, обычно, код должен быть написан таким образом, чтобы это не нужно было использовать, - в частности, весь ввод должен проверяться.

Блок Try/Catch можно применять также для создания своих собственных ошибок:

function setAge(x){ if(typeof(x)=='undefined') throw('Вы должны ввести возраст'); if(typeof(x)!='number') throw('Возраст должен быть числом'); if(x<0) throw('Возраст не может быть меньше 0'); if(x>120) throw('Возраст не может быть больше 120'); var myAge = x; // еще код } try{ setAge(userInput);}catch(err){ alert(err);}

В этом случае выполняется проверка того, что пользователь вводит возраст. Если он вводит недопустимые данные, сценарий немедленно завершается, а пользователь получает сообщение об ошибке.

Блок try/catch имеет еще одну часть, оператор finally:

try{ // код }catch(err){ // код }finally{ // код }

Код в "завершающем блоке" будет выполняться независимо от того, что происходит с операторами Try/Catch. В чем же разница между завершающим блоком и простым размещением кода после блока try/catch? В большинстве случаев никакой разницы не будет. Однако, если блок try/catch находится в функции и происходит выход из функции в блоке try или catch, то возникнет существенное различие:

function myFunction(){ try{ return someValue; }catch(err){ return defaultValue; }finally{ alert('finally!'); } alert('End!');}

В этом случае оба блока try и catch возвращают значение. Мы получим сообщение " finally!", но не получим сообщение " End!", потому что произойдет выход из функции до сообщения alert('End!'). То же самое остается справедливым для операторов Try/Catch, которые осуществляют выход из тела цикла for или while, например:

for(var i=0; i<10; i++){ try{ if(i==5) continue; }catch(err){ // обработка ошибки   }finally{ // код   } // еще код }

Обработка ошибок в AJAX

Запросы XMLHttp, рассмотренные в "предыдущей лекции" , могут иметь совершенно другой тип ошибки: данные просто не проходят. Это можно проверить через статус объекта XMLHttp:

function processingFunction(){ if(oXml.readyState!=4) return; // запрос не выполнен switch(oXml.status){ case 0: case 200: // запрос выполнен       break; case 408: case 504: // запрос превысил время ожидания       // код       break; default: // ошибка запроса       // код return; // возможно, вы захотите выйти       break; } // продолжение обработки запроса }

oXml в этом примере является объектом XMLHttp, а функция processingFunction была присоединена к свойству onreadystatechangeэтого объекта.

Проверяя код статуса, мы узнаем, был ли запрос обработан успешно. Код 200 является в HTTP стандартным кодом статуса "Все прошло нормально" . Код 0 возникает при загрузке файлов из локальной файловой системы (если для этого есть соответствующие полномочия). Статус код 0 часто возникает при локальном тестировании приложения.

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

Все другие коды ошибок имеют свои собственные значения, но в данной ситуации это не важно. Нас интересует только то, что мы не получили нужные данные. Поэтому если код попадает в область " default ", то мы имеем проблему. Наверно в этой ситуации лучше всего сообщить пользователю о проблеме и выйти из функции.

Это почти все об обработке ошибок в JavaScript. Имеет смысл включать в функции обработку ошибок, но, возможно, что это не требуется для каждой функции или каждого фрагмента кода. В большинстве ситуаций достаточно проверки ввода пользователей. Для реализации проверки пользователя наиболее полезным средством является использование блоков Try/Catch/Throw.

1.12. Рекурсия.

"Чтобы понять рекурсию, сначала необходимо понять рекурсию".

Данное высказывание очень четко выражает суть рекурсии. Рекурсия является базовой концепцией программирования вообще, а не только JavaScript, понимание которой очень полезно. Она включает вызов функции из той же самой функции. Почему это может понадобиться? Предположим, что имеется массив массивов. Каждый из этих массивов может иметь в себе массивы, которые могут иметь массивы, которые могут иметь ... собственно, в этом и состоит идея. Таким образом мы имеем множество массивов в других массивах. Как выполнить одну и ту же операцию на всех элементах во всех этих массивах? Можно попробовать использовать простой цикл for, но неизвестно, сколько имеется массивов, и неизвестно, как глубоко распространяется вложение массивов. Поэтому остается только концепция рекурсии.

В этом примере мы циклически перебираем все элементы массива первого уровня. Если какой-либо из этих элементов в массиве сам будет массивом, мы снова вызываем функцию, передавая этот элемент как первичный массив. Затем функция будет перебирать этот массив и снова вызывать себя, пока не останется ни одного массива.

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

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

Простым примером будет написание рекурсивной функции факториала. Факториал N, записываемый как N!, определяется как произведениевсех чисел от N до 1. Поэтому 5! будет равен 5*4*3*2*1 = 120.

function factorial(N){   return N<=1?1:N*factorial(N-1);}

Демонстрационный пример

Факториал

Очень элегантное решение, не правда ли? В результате мы вызываем функцию факториала 5 раз для N = 5. Можно в действительности развернуть всю рекурсивную функцию и получить (5*(4*(3*(2*(1))))). Каждая пара скобок представляет новый вызов функции факториала. Можно также видеть, что если пользователь вводит число <=1, то всегда получит в качестве результата 1.

Давайте рассмотрим другой пример. При работе с программами рисования, такими, как Photoshop или MS Paint, иногда используется инструмент заливки ( flood fill ). Этот инструмент заливает выбранный цвет другим, указанным цветом. Это делается рекурсивно, и алгоритм заливки достаточно прямолинеен:

/*это - псевдо-код, быстро написанный и нефункциональный, который должен просто дать общую идею о том, как действует реальный код*/ function floodFill(x, y){   if(alreadyFilled(x, y)) return;   fill(x, y);    floodFill(x, y-1);   floodFill(x+1, y );   floodFill(x, y+1);   floodFill(x-1, y );} function fill(x, y){   // эта функция будет фактически изменять цвет поля } function alreadyFilled(x, y){   // эта функция проверяет, что поле уже было закрашено }

Идея этого кода состоит в том, чтобы закрасить текущий квадрат или пиксель. Затем он пытается закрасить квадрат выше, справа, ниже и слева от себя. При таком алгоритме каждый квадрат будет закрашен достаточно быстро. Однако здесь возникает небольшая проблема - размер стека.

Стек

При вызове функции копия множества переменных, которые нужны ей для работы, сохраняется в памяти. Если функция вызывается рекурсивно, то другая копия всех этих переменных сохраняется в памяти, затем еще одна и т.д. Эти копии переменных сохраняются в так называемом стеке. Первая копия находится внизу, следующая поверх нее, и т.д. К сожалению, существует ограничение на размер стека. В большинстве браузеров этот предел определен где-то в районе 1000. Это означает, что для функции заливки сетка могла бы содержать до 1000 квадратов. Некоторые браузеры имееют меньший размер стека. Web-браузер Safari, например, имеет максимальный размер стека, равный примерно 100, поэтому даже небольшая сетка 10 х 10 исчерпает возможности браузера.

Таким образом мы имеем ограничение в самом браузере, которое определяет, что при использовании рекурсии можно углубиться только на 1000 уровней. К сожалению, наш алгоритм floodFill (заливки) будет в худшем случае углубляться на 1000 уровней на сетке, содержащей только 1000 полей. Если имеется что-то более сложное, то существует вероятность, что произойдет переполнение стека и просто остановка выполнения кода. Очевидно, что существуют ситуации, когда необходимо закрасить область, содержащую более 1000 пикселей. Что делать в таком случае? Попробуем изменить подход.

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

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

Создание собственного стека

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

var Stack = []; function floodFill(x, y){   fillPixel(x, y);    while(Stack.length>0){          toFill = Stack.pop();          fillPixel(toFill[0], toFill[1]);   }} function fillPixel(x, y){   if(!alreadyFilled(x, y)) fill(x, y);    if(!alreadyFilled(x, y-1)) Stack.push([x, y-1]);   if(!alreadyFilled(x+1, y )) Stack.push([x+1, y ]);   if(!alreadyFilled(x, y+1)) Stack.push([x, y+1]);   if(!alreadyFilled(x-1, y )) Stack.push([x-1, y ]);} function fill(x, y){   // эта функция будет фактически изменять цвет поля } function alreadyFilled(x, y){   // эта функция проверяет, что квадрат еще не был закрашен }

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

Отметим, что существует очень немного рекурсивных функций, которые будут реально переполнять стек JavaScript. В действительности это не совсем верно. Почти любая рекурсивная функция может переполнить стек, но чаще всего данные, которые функция получает от пользователя, делают это крайне маловероятным событием. Когда, например, вы встретите документ XML с глубиной вложения узлов, равной 1000? Почти наверняка - никогда.

Применение рекурсии

Теперь, имея некоторое представление о рекурсии, надо попытаться понять, что с ней можно делать, кроме закрашивания изображений. Одним из наиболее распространенных рекурсивных алгоритмов будет так называемый двоичный поиск. Это можно представить себе как игру на больше-меньше. Если кто-то предлагает угадать число от 1 до 100, то, скорее всего, начать лучше с 50. Если партнер скажет, что больше, то можно предположить 75 и т.д. Это можно продолжать, пока число не будет найдено.

Как оказывается, это один из самых быстрых способов поиска данных, когда известно, что все данные уже отсортированы. Если имеется списокиз 1000000 позиций и требуется найти одну из них, то можно начать с 500000 и использовать тот же процесс, что и в игре "больше-меньше".

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

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

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

 

 

2. Практическая часть  
2.1. Решение задач по примерам готовых скриптов вариант №5  
2.1.1. Задача №2    

Вместо картинки за курсором мыши должна «бегать» строка произвольного содержания.

<html>

<head>

<title>HTML-документ</title>

</head>

<script language="JavaScript" type="text/javascript">

ns4 = (document.layers)? true:false

ie4 = (document.all)? true:false

 

function init() {

if (ns4) {document.captureEvents(Event.MOUSEMOVE);}

document.onmousemove=mousemove;

}

function mousemove(event) {

   var mouse_x = y = 0;

if (document.attachEvent != null) {

   mouse_x = window.event.clientX;

   mouse_y = window.event.clientY;

} else if (!document.attachEvent && document.addEventListener) {

   mouse_x = event.clientX;

   mouse_y = event.clientY;

}

status="x = " + mouse_x + ", y = " + mouse_y;

document.getElementById('xy').innerHTML = "x = " + mouse_x + ", y = " + mouse_y;

  

 document.getElementById('x1').style.left = mouse_x;

document.getElementById('x1').style.top = mouse_y;

 

}

init()

</script>

 

<body >

<i>Internet</i>

 

<span id="xy"></span>

<br>

   <div id="x1" style="position:absolute;width:50%;height:50%;">

      <img src="point.gif">

   </div>

</body>

</html>

 

2.1.2. Задача №8

 

Добавить к калькулятору следующую возможность: Вычисление натурального логарифма числа.

<HTML>

<HEAD>

<TITLE>Calculator</TITLE>

<META http-equiv="Content-Type" content="text/html; charset=windows-1251">

<STYLE TYPE="text/css">

 BODY {

background: F5DEB3;

color: 8B4513;

 }

 INPUT {width: 45px}

</STYLE>

<SCRIPT Language="JavaScript">

 

var num1=0, num2=0, oper=0;

 

function plug()

{

document.Calc.monitor.value='';

num1=0; num2=0; oper=0;

document.Calc.b0.disabled=0; document.Calc.b1.disabled=0; document.Calc.b2.disabled=0;

document.Calc.b3.disabled=0; document.Calc.b4.disabled=0; document.Calc.b5.disabled=0;

document.Calc.b6.disabled=0; document.Calc.b7.disabled=0; document.Calc.b8.disabled=0;

document.Calc.b9.disabled=0; document.Calc.div.disabled=0; document.Calc.off.disabled=0;

document.Calc.mult.disabled=0; document.Calc.sqr.disabled=0; document.Calc.sqrt.disabled=0;

document.Calc.subt.disabled=0; document.Calc.fix.disabled=0; document.Calc.res.disabled=0;

document.Calc.add.disabled=0;

};

 

function shut()

{

document.Calc.monitor.value='';

num1=0; num2=0; oper=0;

document.Calc.b0.disabled=1; document.Calc.b1.disabled=1; document.Calc.b2.disabled=1;

document.Calc.b3.disabled=1; document.Calc.b4.disabled=1; document.Calc.b5.disabled=1;

document.Calc.b6.disabled=1; document.Calc.b7.disabled=1; document.Calc.b8.disabled=1;

document.Calc.b9.disabled=1; document.Calc.div.disabled=1; document.Calc.off.disabled=1;

document.Calc.mult.disabled=1; document.Calc.sqr.disabled=1; document.Calc.sqrt.disabled=1;

document.Calc.subt.disabled=1; document.Calc.fix.disabled=1; document.Calc.res.disabled=1;

document.Calc.add.disabled=1;

};

 

function digit(num)

{

document.Calc.monitor.value+=num;

};

 

function squar(check)

{

var val=document.Calc.monitor.value;

(check) ? val=val*val : val=Math.sqrt(val);

document.Calc.monitor.value=val;

};

 

function expr(op)

{

num1=document.Calc.monitor.value;

document.Calc.monitor.value='';

if (num1!=false) oper=op;

 else alert('Введите значение');

};

 

 

function result()

 {

var R, c=1;

num2=document.Calc.monitor.value;

switch (oper) {

case 1 : R=num1/num2; break;

case 2 : R=num1*num2; break;

case 3 : R=num1-num2; break;

case 4 : R=num1*c+num2*c; }

document.Calc.monitor.value=R;

};

function ln()

 {

num1=document.Calc.monitor.value;

document.Calc.monitor.value = Math.log(num1);

};

</SCRIPT>

</HEAD>

<BODY>

<FORM name="Calc">

 <DIV align="center" size="200">

 <H3>Calculator</H3>

<TABLE frame="box" border="2">

<TR><INPUT type="text" name="monitor" style="width: 260px; text-align: right"></TR>

<TR>

<TD><INPUT type="button" name="b7" value="7" disabled="1" onClick="digit(7)"></TD>

<TD><INPUT type="button" name="b8" value="8" disabled="1" onClick="digit(8)"></TD>

<TD><INPUT type="button" name="b9" value="9" disabled="1" onClick="digit(9)"></TD>

<TD><INPUT type="button" name="div" value="/" disabled="1" onClick="expr(1)"></TD>

<TD><INPUT type="button" name="off" value="OFF" disabled="1" onClick="shut()"></TD>

</TR>

<TR>

<TD><INPUT type="button" name="b4" value="4" disabled="1" onClick="digit(4)"></TD>

<TD><INPUT type="button" name="b5" value="5" disabled="1" onClick="digit(5)"></TD>

<TD><INPUT type="button" name="b6" value="6" disabled="1" onClick="digit(6)"></TD>

<TD><INPUT type="button" name="mult" value="*" disabled="1" onClick="expr(2)"></TD>

<TD><INPUT type="button" name="sqr" value="SQR" disabled="1" onClick="squar(1)"></TD>

</TR>

<TD><INPUT type="button" name="b1" value="1" disabled="1" onClick="digit(1)"></TD>

<TD><INPUT type="button" name="b2" value="2" disabled="1" onClick="digit(2)"></TD>

<TD><INPUT type="button" name="b3" value="3" disabled="1" onClick="digit(3)"></TD>

<TD><INPUT type="button" name="subt" value="-" disabled="1" onClick="expr(3)"></TD>

<TD><INPUT type="button" name="sqrt" value="SQRT" disabled="1" onClick="squar(0)"></TD>

<TD><INPUT type="button" name="log" value="ln" onClick="ln()"></TD>

  

</TR>

<TR>

<TD><INPUT type="button" name="b0" value="0" disabled="1"></TD>

<TD><INPUT type="button" name="fix" value="." disabled="1" onClick="digit('.')"></TD>

<TD><INPUT type="button" name="res" value="=" disabled="1" onClick="result()"></TD>

<TD><INPUT type="button" name="add" value="+" disabled="1" onClick="expr(4)"></TD>

<TD><INPUT type="button" name="fault" value="ON/C" onClick="plug()"></TD>

 

</TR>

</TABLE>

 </DIV>

</FORM>

</BODY>

</HTML>

 

2.1.3. Задача №17.

 

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

<HTML>

<HEAD>

<META http-equiv="Content-Type" content="text/html" charset="utf-8">

<TITLE>Календарь</TITLE>

<STYLE TYPE="text/css">

 BODY

{

background: 9999FF;

color: 000099;

}

</STYLE>

<SCRIPT language="JavaScript">

 

function setToday()

{

var now = new Date();

var month = now.getMonth();

var year = now.getYear();

document.Control.month.selectedIndex = month;

document.Control.year.selectedIndex = year - 1900;

formCalendar(month, year);

}

 

function setPrev()

{

var month = document.Control.month.selectedIndex - 1;

document.Control.month.selectedIndex = month;

var year = document.Control.year.selectedIndex + 1900;

formCalendar(month, year);

}

 

function setNext()

{

var month = document.Control.month.selectedIndex + 1;

document.Control.month.selectedIndex = month;

var year = document.Control.year.selectedIndex + 1900;

formCalendar(month, year);

}

 

function selectDate()

{

var year = document.getElementById("year").value;

if(year==""){year=1970}

var month = document.Control.month.selectedIndex;

formCalendar(month, year);

}

 

function getDaysInMonth(_month,_year)

{

var days;

var daysInMonth = new Array(31,28,31,30,31,30,31,31,30,31,30,31);

days = daysInMonth[_month];

if (_month==2 && isLeapYear(_year)) days = 29;

return days;

}

 

function isLeapYear (Year)

{

if (((Year % 4)==0) && ((Year % 100)!=0) || ((Year % 400)==0))

return true;

else return false;

}

 

 

 

function formCalendar(month, year)

{

month = parseInt(month);

year = parseInt(year);

D = new Date(year,month,0);

var temp;

temp = "<TABLE ID='calTab' cellspacing=0 cellpadding=0 border=0 >";

temp += "<TBODY ID='calBody' align='center'>";

temp += "<TR><TD height=25>Пн<TD

temp += "Чт<TD

var n = D.getDay();

for (i=0; i<n; i++)

{

temp += "<TD>&nbsp</TD>";

}

var num = getDaysInMonth(month,year);

for (i=1; i<=num; i++)

{

if ((i+n)%7 || !i) temp += "<TD height=20>" + i + "</TD>";

       else temp += "<TD>" + i + "</TD></TR><TR>";

}

D = new Date(year,month,num-1);

num = D.getDay();

for (i=num; i<6; i++)

{

temp += "<TD>&nbsp</TD>";

}

temp += "</TR></TBODY></TABLE>";

document.all["calTab"].outerHTML = temp;

}

</SCRIPT>

</HEAD>

<BODY onLoad="setToday()">

<CENTER>

<H2>Календарь</H2>

<FORM name="Control">

<SELECT name="month" onChange="selectDate()">

<OPTION>Январь</OPTION>

       <OPTION>Февраль</OPTION>

<OPTION><h2>Март</h2></OPTION>

       <OPTION>Апрель</OPTION>

<OPTION>Май</OPTION>

       <OPTION>Июнь</OPTION>

<OPTION>Июль</OPTION>

       <OPTION>Август</OPTION>

<OPTION>Сентябрь</OPTION>

       <OPTION>Октябрь</OPTION>

<OPTION>Ноябрь</OPTION>

       <OPTION>Декабрь</OPTION>

</SELECT>

           

&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp

<INPUT type=text ID = "year" name="year" oninput="selectDate()" size="4" maxlength="4">

</INPUT>

       <SCRIPT>

           

           

document.getElementById("year").onkeypress = function(e) {

e = e || event;

if (e.ctrlKey || e.altKey || e.metaKey) return;

var chr = getChar(e);

// с null надо осторожно в неравенствах, т.к. например null >= '0' => true!

// на всякий случай лучше вынести проверку chr == null отдельно

if (chr == null) return;

if (chr < '0' || chr > '9') {

   return false;

}

 

}

 

function getChar(event) {

if (event.which == null) {

   if (event.keyCode < 32) return null;

   return String.fromCharCode(event.keyCode) // IE

}

 

if (event.which != 0 && event.charCode != 0) {

   if (event.which < 32) return null;

   return String.fromCharCode(event.which) // остальные

}

 

return null; // специальная клавиша

}

 

       </SCRIPT>

           

</FORM>

<FORM name="Calend">

<TABLE ID="calTab" cellspacing=0 cellpadding=0 border=1 style="font-family: serif;">

  <TBODY ID="calBody"></TBODY>

</TABLE>

</FORM>

<FORM name="Buttons">

<INPUT type=button name="prev" value=" << " onClick="setPrev()">

<INPUT type=button name="today" value="Сегодня"  

onClick="setToday()">

<INPUT type=button name="next" value=" >> " onClick="setNext()">

</FORM>

</CENTER>

</BODY>

</HTML>

 

 

2.1.4. Задача №20

Добавить к часам следующую возможность: Вывод времени в 24-часовом формате.

<html>

<head>

<title>Clock</title>

<script language="JavaScript">

var timer=null;

var timerrun=false;

var eat;

function stoptime() {

if(timerrun)

clearTimeout(timer);

timerrun=false;

}

var eat = prompt('Введите режим времени 12 или 24');

var eaten = parseInt(eat);

if(eaten == 24){

 

function starttime() {

stoptime();

showtime();

}

function showtime() {

var all=new Date();

var hours=all.getHours();

var minutes=all.getMinutes();

var seconds=all.getSeconds();

var timevalue=" " + hours

timevalue += ((minutes<10) ? " :0" : " : ") + minutes

timevalue += ((seconds<10) ? " :0" : " : ") + seconds

timevalue +=      " 24H "

document.clock.next.value=timevalue;

timer=setTimeout('showtime()',1000);

timerrun=true;

}}

else

{function starttime() {

stoptime();

showtime();

}

function showtime() {

var all=new Date();

var hours=all.getHours();

var minutes=all.getMinutes();

var seconds=all.getSeconds();

var timevalue=" " + ((hours>12) ? hours-12 : hours)

timevalue += ((minutes<10) ? " :0" : " : ") + minutes

timevalue += ((seconds<10) ? " :0" : " : ") + seconds

timevalue += (hours>=12) ? " P.M. " : " A.M. "

document.clock.next.value=timevalue;

timer=setTimeout('showtime()',1000);

timerrun=true;

}}

</script>

<body bgcolor=F5DEB3 text=8B4513 onLoad="starttime()">

<center>

<H3>Clock</H3>

<form name=clock>

<input type=text name=next size=12 value=' '>

</center>

</form>

</body>

</html>

 

Выводы

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

- навыки программирования на JavaScript;

- навыки настройки параметров JavaScript для интерактивных веб-страниц;

- разработка JavaScript как с нуля, так и доработка существующего кода под определённые цели;

Практическое изучение JavaScript.

Также получены следующие первичные умения и навыков научно-исследовательской деятельности:

- умения применять методики использования программного обеспечения для исследования и отладки программного кода JavaScript.

Список использованной литературы

1. Jeremy, McPeak JavaScript 24–Hour Trainer / Jeremy McPeak. - Москва: Гостехиздат, 2010. - 360 c.

2. Бибо, Бер jQuery. Подробное руководство по продвинутому JavaScript / Бер Бибо , Иегуда Кац. - М.: Символ-плюс, 2009. - 384 c.

3. Дронов, Владимир JavaScript и AJAX в Web-дизайне / Владимир Дронов. - М.: "БХВ-Петербург", 2012. - 736 c.

4. Дунаев, Вадим Сценарии для Web-сайта. PHP и JavaScript / Вадим Дунаев. - М.: "БХВ-Петербург", 2008. - 576 c.

4. Кингсли-Хью, К.Э. JavaScript 1.5: учебный курс / К.Э. Кингсли-Хью. - М.: СПб: Питер, 2002. - 272 c.

6. Климов, Александр JavaScript на примерах / Александр Климов. - М.: "БХВ-Петербург", 2009. - 336 c.

7. Лазаро, Исси Коэн Полный справочник по HTML, CSS и JavaScript / Лазаро Исси Коэн, Джозеф Исси Коэн. - М.: ЭКОМ Паблишерз, 2007. - 532 c.

8. Макфарланд, Дэвид JavaScript. Подробное руководство / Дэвид Макфарланд. - М.: Эксмо, 2009. - 608 c.

9. Мартынов, Н. Н. Алгоритмизация и основы объектно-ориентированного программирования на JavaScript. Информатика и ИКТ. Профильный уровень. 10 класс / Н.Н. Мартынов. - М.: Бином-Пресс, 2010. - 272 c.

10. Прохоренок, Николай HTML, JavaScript, PHP и MySQL. Джентльменский набор Web-мастера (+ CD-ROM) / Николай Прохоренок. - М.: БХВ-Петербург, 2010. - 912 c.

11. Флэнаган, Д. JavaScript: подробное руководство / Д. Флэнаган. - М.: Символ, 2008. - 992 c.

12. Чаффер, Джонатан Изучаем jQuery 1.3. Эффективная веб-разработка на JavaScript / Джонатан Чаффер , Карл Шведберг. - М.: Символ-плюс, 2010. - 448 c.

 


Дата добавления: 2018-08-06; просмотров: 210; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!