Введение в современные веб-технологии

Сценарии Javascript

Разбить на страницы
Показывать лекцию целиком

Первоначально разработанный в 1995 году Бренданом Эйком (Brendan Eich), работавшим в Netscape, в сотрудничестве с Sun Microsystems, язык сценариев Javascript является объектно-ориентированным языком. Изначально Javascript предназначался для разработки клиентских приложений в веб-браузере с целью расширения пользовательского интерфейса и разработки динамических веб-страниц. В 1997 году язык был стандартизирован Европейской ассоциацией производителей компьютеров (European Computer Manufacturers Association, ECMA) под названием ECMAScript (ECMA-262). Internet Explorer, начиная с версии 3.0 (1996), поддерживает диалект ECMAScript, названный JScript. Как любой живой язык, JScript развивался, и Internet Explorer 8.0 использует версию языка JScript 5.8, основанную на 3 издании стандарта ECMA-262. В целом, диалекты ECMAScript достаточно близки, по этой причине Javascript и JScript часто считаются синонимами.

Несмотря на сходство названий, Javascript не является родственником Java, оба языка наследовали свой синтаксис от языка C.

Характеристики языка Javascript

Javascript поддерживает синтаксис структурного программирования языка C, включая ветвление (инструкции if…, if…else… и switch ) и циклы (инструкции for и while ).

Типы данных в Javascript связаны со значениями, а не с переменными. Это означает, что любой переменной может быть присвоено значение одного типа (например, число), затем другого типа (например, строка). Такое поведение иногда называют также нестрогой типизацией.

Javascript почти полностью построен как объектный язык. Фактически, объекты Javascript – это ассоциативные массивы; запись obj.x = 10 эквивалентна obj["x"] = 10 и obj[0] = 10 (полагая, что x является первым по счету свойством объекта obj ). Свойства объекта и их значения могут добавляться, изменяться и удаляться динамически.

Функция eval() позволяет выполнять инструкции, заданные в виде строки.

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

Внутренние функции (определенные внутри других функций) создаются, когда выполняется внешняя функция, и переменные внешней функции доступны для внутренней даже когда внешняя функция завершила работу.

Javascript не использует понятия классов. Наследование осуществляется через механизм прототипов ( prototype ), то есть клонирования существующих объектов.

Функции могут выступать в качестве конструкторов объектов. В этом случае ключевое слово this относится к создаваемому объекту.

В Javascript нет различий между определением функции и метода. Различие возникает, когда функция вызывается как метод объекта. В этом случае ключевое слово this относится к объекту, чьим методом оказывается функция.

Функции в Javascript могут принимать произвольное число аргументов, в том числе и не описанные при определении функции. Доступ к ним осуществляется как через формальные параметры функции, так и через свойство arguments функции.

Вставка сценария в HTML-документ

Сценарий Javascript вставляется в HTML-документ при помощи тэга script.

<script type="text/javascript">
  document.write('Hello from Javascript');
</script>

Сценарий может также храниться в отдельном файле.

<script type="text/javascript" src="myscript.js"></script>

Атрибут language элемента script описывает используемый язык сценариев (например, Javascript). Этот атрибут считается устаревшим и не рекомендуется к использованию.

Элементы script могут присутствовать в неограниченном числе внутри элементов head и body HTML-документа.

Переменные и типы данных

Javascript во многом наследовал синтаксис языка C, а это значит, что строчные и заглавные буквы в идентификаторах различаются. Нет необходимости специально объявлять переменные, хотя это является хорошей программистской практикой. Первое появление переменной автоматически является ее объявлением. Имя переменной должно начинаться с буквы или символа подчеркивания (_), последующими символами могут быть буквы, цифры и символ подчеркивания (_). Нельзя использовать в качестве имен переменных зарезервированные слова, такие как if, for, function, new. Для объявления переменной может использоваться необязательное слово var. Его использование обязательно только в том случае, если переменная объявляется внутри функции и пределом видимости этой переменной должна быть эта функция. Переменные, объявленные без использования var, являются глобальными. Примеры правильного объявления:

var test; //объявление без присвоения. Значение переменной undefined
  a = 2;
  var b2 = 3; 
  sum_a_b2 = a + b2;

Примеры недопустимых имен:

var 3piglets // начинается с цифры
var SmithWesson // символ  не допускается
var for // for является зарезервированным словом

В Javascript есть три простых типа данных: строка ( String ), число ( Number ) и логический тип ( Boolean ), два составных типа: объект ( Object ) и массив ( Array ) и два специальных типа: пустое значение ( Null ) и неопределенное значение ( Undefined ). Тип данных ассоциирован не с именем переменной, а с ее значением.

Строковые данные – любая цепочка символов, заключенная в одиночные (') или двойные кавычки ("). Одиночные кавычки могут содержаться в строках, заключенных в двойные кавычки и наоборот.

"простая строка"
'еще одна строка'
"don't worry"

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

Число Десятичное представление Описание
0.0001, .0001, 1e-4, 1.0e-4 0.0001 Разные представления числа с плавающей запятой
125 125 Целое
0377 255 Восьмеричное представление целого
0378 378 Десятичное целое. Восьмеричное представление допускает лишь цифры от 0 до 7
0xff 255 Шестнадцатеричное представление
0x3e7 999 Шестнадцатеричное представление
00.001 0x0.001 - Ошибка компиляции. Восьмеричные и шестнадцатеричные числа могут быть только целыми

Данные логического (Boolean) типа принимают лишь одно из двух значений: true (истина) или false (ложь).

y = (x == 10); //истина, если x равно 10

Чаще всего логические данные или выражения, возвращающие логические данные, используются в управляющих структурах:

if (x == 10)
    a = c; // выполняется, если x равно 10
  else
    a = b; // выполняется в противном случае

В качестве логического может быть использовано любое выражение, например, число, строка, массив или объект, при этом, если значение равно 0, пустой строке, пустому (не содержащему элементов) массиву, пустому объекту ( Null ) или неопределенному значению ( undefined ), то условие оценивается как false (ложь), в противном случае как true (истина). Например, две следующие конструкции эквивалентны.

if (x != 0)//если x не равен 0
      a = 1;

    if (x)
      a = 1;

Массив представляет собой данные, доступ к элементам которых осуществляется по индексу. Массивы в Javascript создаются следующим образом:

var a = new Array();// создает новый пустой массив
var b = new Array(N);//где N – положительное целое. 
//Создает массив из N пустых элементов
var c = new Array(elem1, elem2 ...);
//создает массив из перечисленных элементов. 
//Элементы должны существовать заранее.
var d = [elem1, elem2 ...];
//создает массив из перечисленных элементов
var e = [];// создает новый пустой массив

Индексация массивов всегда начинается с нуля (0), то есть последний существующий элемент имеет индекс на 1 меньше длины массива.

document.write(c[0]); //будет выведен первый элемент массива c
    a[0] = 1; // присвоение значения первому элементу массива a
    a[10] = 10; // присвоение значения одиннадцатому элементу массива a
    // элементы 1 – 9 заполняются пустыми значениями.
    // длина массива a.length равна 11
    document.write(a[10]); // печатает 10
    document.write(a[11]); //печатает пустую строку и не меняет длину массива

Нет необходимости переопределять длину массива. Присвоение значения несуществующему элементу массива автоматически создает этот элемент (если его не было) и заполняет массив пустыми элементами между последним существовавшим и вновь добавленным элементом.

Операторы и управляющие структуры

Javascript поддерживает арифметические, логические и битовые операторы и ряд вспомогательных операторов.

Арифметические операторы

Операции сложения (+), вычитания (-), умножения (*) и деления (/) выполняются в общепринятой манере.

x = ((1 + 2)*3 – 4)/5; // 1

Оператор деления с остатком (%) делит первое число на второе и возвращает остаток

a = 9 % 4; // 1
    b = 19 % 6.7; // 5.6

В Javascript нет операции целочисленного деления. Чтобы получить целое частное, следует отсечь десятичные знаки.

dividend = 13; // делимое
    divisor = 5; // делитель
    quotient = parseInt(dividend / divisor); // частное (2)
    reminder = dividend % divisor; // остаток (3)

Оператор плюс (+) требует некоторого дополнительного внимания. Помимо арифметического сложения этот оператор выполняет также конкатенацию (строковое слияние). Результат может иногда оказаться неожиданным (для начинающего разработчика).

a = 1;
    b = '2';
    c = a + b; // 12

Строковое слияние происходит всегда, когда хотя бы один из операндов – строка. Остальные арифметические операторы пытаются сначала преобразовать строку в число и только в случае неуспеха возвращают ошибку NaN (Not a Number).

Арифметические операторы могут быть объединены с оператором присваивания (=), что является удобной сокращенной записью операций.

a += 3; // эквивалентно a = a + 3
    b /= 2; // b = b / 2
    c = 'Hello';
    c += ' world!'; // Hello world

Есть также три одноместных оператора: смены знака (-), инкремента (++) и декремента (--).

x = -x; // смена знака
    x++; // эквивалентно x += 1 или x = x + 1
    --x; // эквивалентно x -= 1 или x = x – 1

Операторы инкремента и декремента могут иметь префиксную ( ++x ) и постфиксную ( x++ ) формы. В префиксной форме переменная сначала изменяется, затем используется, в постфиксной сначала используется, затем изменяется.

var a, b, c, d;
    a = b = 2;
    // так можно, поскольку оператор присваивания возвращает значение
    c = ++a; // 3
    d = b++; // 2

Логические операторы

Операторы сравнения производят сравнение числовых или строковых операндов и возвращают логическое значение. При сравнении строки рассматриваются как цепочки 16-битных символов. Следует иметь в виду, что в таблицах символов заглавные буквы идут впереди строчных, таким образом "Я" оказывается меньше "автор". Для правильного алфавитного сравнения строки следует привести к верхнему или нижнему регистру.

Операторы > (больше), < (меньше), >= (больше или равно), <= (меньше или равно):

  • Пытаются привести оба операнда к числам
  • Если оба операнда – строки, выполняют строковое сравнение
  • Если любой из операндов имеет значение NaN (Not a Number), возвращают false (ложь)
  • $$-0 == +0$$
  • $$-\infty$$ < $$-\infty$$ < любое число
  • $$+\infty$$ > $$+\infty$$ > любое число
  • Операторы == (равно), != (не равно):

  • Если типы данных различны, пытаются привести их к строке, числу или логическому типу
  • NaN != NaN
  • -0 == +0
  • null == null, null == undefined
  • 1 == '1', 1 == true, 0 == false, true != 'true'
  • Операторы сравнения на идентичность ( === ) и неидентичность ( !== ). Для идентичности операнды должны быть одного типа и указывать на один объект.

    Логическое отрицание ( !) меняет истину на ложь и наоборот.

    Логическое И ( ) возвращает истину, только когда оба операнда истинны.

    Логическое ИЛИ ( || )возвращает истину, только когда хотя бы один из операндов истина.

    "Тройной" условный оператор позволяет сократить запись при присваивании значения по условию:

    var x = 3;
      result = x % 2 == 0 ? "четное" : "нечетное";
      //эквивалентно
      if (x % 2 == 0)
        result = "четное";
      else
        result = "нечетное";

    Управляющие структуры

    Ветвящиеся структуры, if, if…else и switch, позволяют направить выполнение программы в соответствии с проверяемым условием.

    if (b != 0)
          c = a / b; //выполнить деление только если делитель не ноль
    
        if (x % 2 == 0)
          document.write(x + ' четное');
        else
          document.write(x + ' нечетное');
    
        var d = new Date(); // объект Date – текущая дата и время
        switch (d.getDay()) {
          case 0:
            document.write("Воскресенье");
            break; //выход из переключателя. 
          //Если опущен, будет выполнена и следующая инструкция  
          case 1:
            document.write("Понедельник");
            break;
          case 2:
            document.write("Вторник");
            break;
          case 3:
            document.write("Среда");
            break;
          case 4:
            document.write("Четверг");
            break;
          case 5:
            document.write("Пятница");
            break;
          case 6:
            document.write("Суббота");
            break;
          default: // выполняется, если не выполнено 
            //ни одно из предыдущих условий
            document.write(d.getDay());
        }

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

    Цикл for управляется счетчиком.

    var a = []; // объявляем массив
        for (var i = 0; i < 10; i++)
          a[i] = i; // заполняем 10 элементов массива
        var sum = 0;
        for (var j = 0, N = a.length; j < N; j++)
          sum += a[j];
        document.write('Сумма = ' + sum);

    Специальная форма for, for/in, удобна для перебора свойств объекта.

    var nav = window.navigator; // информация о браузере
        for (prop in nav)
          document.write(prop + ': ' + nav[prop] + '<br>\n');
        // печатает построчно пары свойство: значение объекта.

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

    var x = 0;
        while (x != 4) {//цикл повторяется, пока условие истинно
          x = window.prompt('Сколько будет 2 * 2', x);
          //метод window.prompt открывает диалоговое окно с полем ввода,
          //куда нужно ввести ответ. 
        }
        if (x == 4)
          window.alert("Правильно");

    В цикле do…while условие проверяется в конце цикла, то есть цикл повторяется как минимум один раз.

    var x = 0;
        do {
          x = window.prompt('Сколько будет 2 * 2', x);
        } while (x != 4); //цикл повторяется, пока условие истинно
        if (x == 4)
          window.alert("Правильно");

    В некоторых случаях требуется выход из цикла или переход к началу цикла до завершения всего блока инструкций. Оператор break заканчивает выполнение цикла и передает управление первой инструкции после цикла. Оператор continue пропускает инструкции до конца блока и начинает новый повтор цикла.

    var x = 0;
        while (x != 4) {
          x = window.prompt("Сколько будет 2*2?", x);
    
          if (x == null)//пользователь нажал "Отмена"
            break; // выход из цикла
    
          if (Number(x) == x)// является ли x числом
            continue; // пропускаем остаток и начинаем новый повтор
          // с проверкой условия
    
          //сюда попадаем, только если введено не число
          window.alert("Вводите только числа");
        } // конец цикла
    
        if (x == null)
          window.alert("Вы сдались");
        else
          window.alert("Вы угадали");

    Встроенные объекты Javascript

    Javascript поддерживает ряд собственных объектов, среди которых обратим внимание на следующие:

    Объект Object содержится во всех остальных объектах и обеспечивает их общую функциональность. Уже упомянутые числа, строки и массивы являются объектами Number, String и Array, соответственно, и каждый из этих объектов имеет свой набор свойств и методов. Объект Math открывает доступ к алгебраическим и тригонометрическим функциям. Объект Date предназначен для операций с датой и временем; при работе в браузере доступны лишь функции чтения данных, такие как getDate, getDay, но не setDate или setTime. Объект Global создается при запуске движка Javascript, его свойства, такие как Infinity (бесконечность), NaN (ошибка Not a Number) и undefined (неопределенная переменная), и методы доступны автоматически.

    var a = '3'; //строка
        var b = 4; //число
        var c = a + b; //34 – строковое слияние
        var d = parseFloat(a) + b; //7
        //функция parseFloat – читать как число с плавающей запятой – 
        //гарантирует, что строка, содержащая число, будет интерпретирована
        //как число
    
        var str = 'var x = 2 + 3;';
        eval(str);
        document.write(x); //5
        //функция eval выполняет код, заданный в виде строки, 
        //которая может генерироваться динамически

    Особое место занимает объект Function (функция). Функции в Javascript являются объектами первого класса, то есть могут присваиваться переменным, выступать аргументами других функций и быть результатом (возвращаемым) функций. В Javascript есть несколько вариантов синтаксиса создания функций.

    //классический синтаксис
        function a1(x, y) {
          return x + y;
        }
    
        //явное создание объекта Function
        var a2 = new Function('x', 'y', 'return x + y');
        //обязательным является только последний аргумент – тело функции
    
        //еще один вариант
        var a3 = function(x, y) { return x + y; };

    Независимо от способа задания функции ее вызов осуществляется одинаково и с одинаковым результатом.

    a = a1(3, 4);
        b = a2(3, 4);
        c = a3(3, 4);

    Функция как объект имеет собственные свойства

    function ArgTest(a, b) {
        var i, s = "Функция ArgTest ожидает ";
        //число аргументов при объявлении функции
        var expargs = ArgTest.length;
    
        //число аргументов при вызове функции
        var numargs = ArgTest.arguments.length;
        if (expargs < 2)
          s += expargs + " аргумент. ";
        else
          s += expargs + " аргумента. ";
        if (numargs == 1)
          s += " Получен 1 аргумент.";
        else
          s += "Получено " + numargs + " аргументов.";
        return (s);
      }

    Массив arguments содержит аргументы, отправленные в функцию при вызове, даже если они не были перечислены при объявлении функции.

    function sum() {
          var s = 0;
          for (var i = 0, n = arguments.length; i < n; i++)
            s += arguments[i];
          return s;
        }
        document.write(sum(1, 2, 3, 4)); //10

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

    //создаем и заполняем массив 10 элементами
      var arr = [];
      for (var i = 0; i < 10; i++)
        arr[i] = i;
    
      //сортируем массив по убыванию
      arr.sort(function(x, y) { return x < y ? 1 : -1; });
      //функция сравнения принимает два аргумента и возвращает -1, 
      //если первый элемент должен идти впереди второго, 
      //и 1 в противном случае
    
      //перетасовываем массив, т.е. сортируем в произвольном порядке
      arr.sort(function() { return Math.random() < 0.5 ? 1 : -1; });
      //от аргументов функции сравнения ничего не зависит, 
      //поэтому они могут быть опущены. 1 и -1 возвращаются 
      //в случайном порядке

    Функции могут вызываться из функций, в том числе из самих себя (рекурсия). Классический пример рекурсии – расчет факториала.

    function Factorial(N) {
          return N < 2 ? 1 : N * Factorial(N - 1);
        }

    Создание пользовательских объектов

    Функции могут служить конструкторами пользовательских объектов. В этом случае ключевое слово this является ссылкой на вновь создаваемый объект.

    //конструктор объекта Окружность
      function Circle(x, y, r) {
        this.x = x; //x-координата центра
        this.y = y; //y-координата центра
        this.r = r; //радиус окружности
      }
    
      //создание объектов
      var circle1 = new Circle(1, 1, 2);
      var circle2 = new Circle(3, 3, 1);

    Свойство prototype функции-конструктора позволяет добавлять свойства и методы как к пользовательским, так и встроенным объектам.

    function getLength() {
          //this является ссылкой на объект, 
          //в контексте которого осуществляется вызов
          return 2 * Math.PI * this.r; //длина окружности
        }
    
        //подключаем функцию к конструктору
        //обратите внимание на отсутствие скобок 
        //без скобок присваивается объект-функция
        //со скобками – результат выполнения функции
        Circle.prototype.length = getLength;
    
        //Добавим еще один метод
        Circle.prototype.area = function() {
          return Math.PI * this.r * this.r; //площадь круга
        }
    
        document.write("circle1 имеет радиус " + circle1.r +
          ", длину " + circle1.length() +
          " и площадь " + circle1.area());

    Использование функции-конструктора позволяют создавать любое число объектов. Если требуется всего один объект, можно воспользоваться альтернативным синтаксисом.

    var uniqueObj = {
          prop1: 10, //определяем числовое свойство
          prop2: 'red', //определяем строковое свойство
          method1: function() { return 'Hello'; } //определяем метод
        };

    Внешние объекты

    Internet Explorer и другие браузеры открывают доступ к собственной объектной модели через программный интерфейс приложения (Application Programming Interface, API), так что свойства, методы и события объектов браузера становятся доступны для Javascript.

    Пример: карточная игра Blackjack

    В качестве примера разработаем карточную игру, известную под названием Blackjack, которая будет работать в веб-браузере.

    Правила игры

    Игра ведется колодой карт из 52 листов, от двойки до туза в каждой из четырех мастей. Каждая карта имеет свою цену, в очках. (От двойки до десятки в соответствии со значением, картинки – 10, туз 11 или 1, в зависимости от набранных очков)

    В игре участвуют два игрока: собственно игрок и крупье. Цель игры - набрать больше очков, чем противник, но не более 21 (за исключением двух тузов).

    Игрок (человек) делает ставку в пределах своего баланса и берет карты по одной, пока не сочтет количество очков достаточным, после чего передает ход крупье.

    Крупье (компьютер) действует автоматически.

    Разработка объектной модели

    Колода состоит из карт. Каждая карта характеризуется мастью, значением и числом очков. Кроме того, уместно привязать к карте ее картинку. Фактически, если перенумеровать все карты в колоде от 0 до 51, то все свойства карты можно рассчитать из ее порядкового номера.

    //конструктор карты
        function Card(n) {
          //вспомогательные массивы
          var suits = ['треф', 'бубен', 'червей', 'пик'];
          var names = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Валет', 'Дама', 'Король', 'Туз'];
          var values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11];
    
          //рассчитываем свойства карты по ее номеру
          this.suit = suits[parseInt(n / 13)]; //масть
          this.name = names[n % 13]; //название
          this.value = values[n % 13]; //очки
    
          //прикладываем картинку к карте - объект Image Веб браузера
          this.img = new Image();
          //рассчитываем имя файла и инициализируем загрузку файла,
          //полагая, что изображения хранятся в папке deck
          //и поименованы от 0.gif для двойки треф
          //до 51.gif для туза пик
          this.img.src = 'deck/' + n + '.gif';
          //задаем дополнительные атрибуты изображения
          this.img.alt = this.toString();
          this.img.className = 'Card';
        }
        // переопределяем функцию toString
        Card.prototype.toString = function() {
          return this.name + ' ' + this.suit;
        }

    Колода – единичный объект. Создадим его для дальнейшего использования.

    var Deck = {
          cards: [], //собственно колода - массив карт, изначально пустой
          num: 0, // внутренний счетчик
    
          //колоду нужно инициализировать перед первым использованием
          init: function() {
            //заполняем массив
            for (var i = 0; i < 52; i++)
            //все данные карты рассчитываются по ее номеру
              this.cards[i] = new Card(i);
          },
    
          //тасуем колоду, т.е. сортируем в случайном порядке
          shuffle: function() {
            this.cards.sort(function() {
                return (Math.random() < 0.5 ? 1 : -1);
              }
            );
            this.num = 0; //инициализируем счетчик
          },
    
          nextCard: function() {
            //возвращает верхнюю карту из колоды
            return this.cards[this.num++];
          }
        };

    В игре участвуют двое. Создадим конструктор для них.

    function Hand() { } // конструктор игрока необходим, даже пустой
    
        Hand.prototype = {//свойства и методы игрока
          balance: 1000, //начальная сумма
          currentBet: 0, //текущая ставка
          cards: [], //массив карт у игрока
          //Методы игрока
          init: function() {//сбросить карты
            this.cards = new Array();
          },
          bet: function() {//увеличиваем ставку
            this.balance += this.currentBet;
            if (this.currentBet == 0)
              this.currentBet = 10; //произвольная начальная ставка
            else
              this.currentBet *= 2; //удваиваем ставку
    
            //ограничим ставку балансом игрока
            if (this.balance >= this.currentBet)
              this.balance -= this.currentBet;
            else {
              this.currentBet = this.balance;
              this.balance = 0
            }
            //возвращаем значение для удобства использования
            return this.currentBet;
          },
          nextCard: function() {//взять следующую карту
            var card = Deck.nextCard(); //следующая карта из колоды
            this.cards.push(card); //добавляем в конец массива
            //возвращаем значение для удобства использования
            return card;
          },
          //подсчитываем очки на руке игрока
          calc: function() {
            var sum = 0; //сумма очков
            var aces = 0; // число тузов
            if (this.cards)//если массив карт не пустой
              for (var i in this.cards) {
              if (this.cards[i].value == 11)
                aces++; //найден туз
              //суммируем очки
              sum += this.cards[i].value;
            }
            //если на руке не более 2 карт, пересчет не нужен
            if (this.cards.length < 3)
              return sum;
            // в случае перебора считаем туза за 1
            while (sum > 21  aces > 0) {
              sum -= 10;
              aces--;
            }
            return sum;
          },
          //сравнение двух игроков
          compare: function(anotherHand) {
            //результат: 1 - победил первый, 
            //-1 - победил второй, 0 – ровно
            //подсчитываем очки первого игрока
            var a = this.calc();
            if (a > 21  this.cards.length > 2)//перебор у первого
              return -1;
            //подсчитываем очки второго игрока
            var b = anotherHand.calc();
            if (b > 21  anotherHand.cards.length > 2)
            // перебор у второго
              return 1;
            if (a == b)//ровно
              return 0;
            if (a == 22  this.cards.length == 2)
            // у первого игрока Black Jack
              return 1;
            if (b == 22  anotherHand.cards.length == 2)
            // у второго игрока Black Jack
              return -1;
            if (a > b)//У первого больше
              return 1;
            //остается единственный вариант - у второго больше
            return -1;
          }
        };

    Объектная модель создана, и мы можем ее использовать. Сохраним весь вышеприведенный код в файле blackjack.js. Создадим HTML-документ ( blackjack.htm ):

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>BlackJack</title>
    
      <script type="text/javascript" src="blackjack.js"></script>
    
      <link href="blackjack.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
      <!-- Весь пользовательский интерфейс располагается здесь -->
    
      <div class="Hand" id="dealer">
      </div>
    
      <div id="result">
      </div>
    
      <div id="player" class="Hand">
      </div>
    
      <div>
        Баланс: $<span id="balance"></span> Ставка: $<span id="bet"></span><br />
        Очки: <span id="sum"></span>
      </div>
    
      <input type="button" onclick="bet()" value="увеличить ставку" id="btnBet" />
      <input type="button" onclick="nextCard()" value="взять карту" id="btnRun" />
      <input type="button" onclick="finish()" value="Крупье" id="btnFinish" />
    </body>
    </html>

    Стилевая информация хранится в отдельном файле blackjack.css. Вот примерное содержание:

    body
    {
        background-color: Green;
        color: White;
        font-family: Comic Sans MS;
        font-size: 14px;
        text-align: center;
    }
    .Card
    {
        border: solid 1px #000;
        margin: 6px;
    }
    .Hand
    {
        height: 120px;/* Делаем достаточно высоким, чтобы поместилась карта */
    }
    #result
    {
        height: 30px;
    }

    Остается создать игроков, инициализировать колоду карт и обработать события нажатия на кнопки.

    //задаем функцию, выполняемую при загрузке страницы
        window.onload = function() {
          Deck.init(); //заполняем колоду картами
          player = new Hand(); //игрок объявлен глобально
          dealer = new Hand(); //крупье объявлен глобально
    
          //кнопки "Взять карту" и "крупье" изначально недоступны
          document.getElementById('btnRun').disabled = true;
          document.getElementById('btnFinish').disabled = true;
          //вспомогательная функция Show() определена ниже
          Show();
        }
        //обрабатываем нажатие кнопки "увеличить ставку"
        function bet() {
          dealer.init(); //сбросить карты у крупье
          player.init(); //сбросить карты у игрока
          Deck.shuffle(); //перетасовать колоду
          //очищаем окно от карт и результата
          document.getElementById('player').innerHTML = '';
          document.getElementById('dealer').innerHTML = '';
          document.getElementById('result').innerHTML = '';
          document.getElementById('sum').innerHTML = '';
          if (player.bet()) {
            // нет необходимости писать if (player.bet() > 0)
            // 0 == false, любое другое число == true
            document.getElementById('btnRun').disabled = false;
          }
          Show();
        }
        //обрабатываем нажатие кнопки "взять карту"
        function nextCard() {
          var oTest = document.getElementById('player');
          var oSum = document.getElementById('sum');
    
          //берем карту и показываем ее в окне браузера
          oTest.appendChild(player.nextCard().img);
          var s = player.calc(); //подсчитываем очки
          oSum.innerHTML = s; //показываем очки
          //делаем доступной карту "Крупье"
          document.getElementById('btnFinish').disabled = false;
          if (s > 21  player.cards.length > 2) {
            //у игрока перебор. нет смысла продолжать
            document.getElementById('result').innerHTML = 'Не повезло';
            player.currentBet = 0;
            document.getElementById('btnFinish').disabled = true;
            document.getElementById('btnRun').disabled = true;
          }
          Show();
        }
        //обрабатываем нажатие кнопки "крупье"
        function finish() {
          document.getElementById('btnRun').disabled = true;
          document.getElementById('btnFinish').disabled = true;
          //находим элемент с id="dealer"
          var oDealer = document.getElementById('dealer');
          //таково внутреннее правило казино - рисковать до 16 очков
          while (dealer.calc() < 17) {
            //добавляем карты крупье и отображаем их
            oDealer.appendChild(dealer.nextCard().img);
          }
          switch (player.compare(dealer)) {
            case 1:
              document.getElementById('result').innerHTML = '!!! Победа !!!';
              player.balance += player.currentBet * 2;
              player.currentBet = 0;
              break;
            case -1:
              document.getElementById('result').innerHTML = 'Не повезло';
              player.currentBet = 0;
              break;
            default: // 0 - ровно
              document.getElementById('result').innerHTML = '??? Ровно ???';
              break;
          }
          Show();
        }
        //вспомогательная функция
        function Show() {
          //находим элемент с id='balance' и показываем баланс игрока
          //у крупье баланс не ограничен
          document.getElementById('balance').innerHTML = player.balance;
          document.getElementById('bet').innerHTML = player.currentBet;
        }

    Этот код можно добавить в файл blackjack.js. Сохраните все файлы в одной папке. Откройте файл blackjack.htm в браузере.

    Вопросы

  • Какое назначение имеет язык Javascript?
  • Каким образом сценарий Javascript вставляется в HTML-документ?
  • Как объявляется переменная? Какие имена недопустимы?
  • Какие типы данных имеются в Javascript?
  • Как создаётся массив? С чего начинается нумерация элементов массива?
  • Перечислите и охарактеризуйте управляющие структуры Javascript.
  • Какие существуют в Javascript варианты синтаксиса создания функций?
  • Как создать несколько однотипных пользовательских объектов?
  • Как добавить метод к типу объекта?
  • Как создаётся объект при отсутствии необходимости определения его типа?
  • Страницы:

    Первоначально разработанный в 1995 году Бренданом Эйком (Brendan Eich), работавшим в Netscape, в сотрудничестве с Sun Microsystems, язык сценариев Javascript является объектно-ориентированным языком. Изначально Javascript предназначался для разработки клиентских приложений в веб-браузере с целью расширения пользовательского интерфейса и разработки динамических веб-страниц. В 1997 году язык был стандартизирован Европейской ассоциацией производителей компьютеров (European Computer Manufacturers Association, ECMA) под названием ECMAScript (ECMA-262). Internet Explorer, начиная с версии 3.0 (1996), поддерживает диалект ECMAScript, названный JScript. Как любой живой язык, JScript развивался, и Internet Explorer 8.0 использует версию языка JScript 5.8, основанную на 3 издании стандарта ECMA-262. В целом, диалекты ECMAScript достаточно близки, по этой причине Javascript и JScript часто считаются синонимами.

    Несмотря на сходство названий, Javascript не является родственником Java, оба языка наследовали свой синтаксис от языка C.

    Характеристики языка Javascript

    Javascript поддерживает синтаксис структурного программирования языка C, включая ветвление (инструкции if…, if…else… и switch ) и циклы (инструкции for и while ).

    Типы данных в Javascript связаны со значениями, а не с переменными. Это означает, что любой переменной может быть присвоено значение одного типа (например, число), затем другого типа (например, строка). Такое поведение иногда называют также нестрогой типизацией.

    Javascript почти полностью построен как объектный язык. Фактически, объекты Javascript – это ассоциативные массивы; запись obj.x = 10 эквивалентна obj["x"] = 10 и obj[0] = 10 (полагая, что x является первым по счету свойством объекта obj ). Свойства объекта и их значения могут добавляться, изменяться и удаляться динамически.

    Функция eval() позволяет выполнять инструкции, заданные в виде строки.

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

    Внутренние функции (определенные внутри других функций) создаются, когда выполняется внешняя функция, и переменные внешней функции доступны для внутренней даже когда внешняя функция завершила работу.

    Javascript не использует понятия классов. Наследование осуществляется через механизм прототипов ( prototype ), то есть клонирования существующих объектов.

    Функции могут выступать в качестве конструкторов объектов. В этом случае ключевое слово this относится к создаваемому объекту.

    В Javascript нет различий между определением функции и метода. Различие возникает, когда функция вызывается как метод объекта. В этом случае ключевое слово this относится к объекту, чьим методом оказывается функция.

    Функции в Javascript могут принимать произвольное число аргументов, в том числе и не описанные при определении функции. Доступ к ним осуществляется как через формальные параметры функции, так и через свойство arguments функции.

    Вставка сценария в HTML-документ

    Сценарий Javascript вставляется в HTML-документ при помощи тэга script.

    <script type="text/javascript">
      document.write('Hello from Javascript');
    </script>

    Сценарий может также храниться в отдельном файле.

    <script type="text/javascript" src="myscript.js"></script>

    Атрибут language элемента script описывает используемый язык сценариев (например, Javascript). Этот атрибут считается устаревшим и не рекомендуется к использованию.

    Элементы script могут присутствовать в неограниченном числе внутри элементов head и body HTML-документа.

    Переменные и типы данных

    Javascript во многом наследовал синтаксис языка C, а это значит, что строчные и заглавные буквы в идентификаторах различаются. Нет необходимости специально объявлять переменные, хотя это является хорошей программистской практикой. Первое появление переменной автоматически является ее объявлением. Имя переменной должно начинаться с буквы или символа подчеркивания (_), последующими символами могут быть буквы, цифры и символ подчеркивания (_). Нельзя использовать в качестве имен переменных зарезервированные слова, такие как if, for, function, new. Для объявления переменной может использоваться необязательное слово var. Его использование обязательно только в том случае, если переменная объявляется внутри функции и пределом видимости этой переменной должна быть эта функция. Переменные, объявленные без использования var, являются глобальными. Примеры правильного объявления:

    var test; //объявление без присвоения. Значение переменной undefined
      a = 2;
      var b2 = 3; 
      sum_a_b2 = a + b2;

    Примеры недопустимых имен:

    var 3piglets // начинается с цифры
    var SmithWesson // символ  не допускается
    var for // for является зарезервированным словом

    В Javascript есть три простых типа данных: строка ( String ), число ( Number ) и логический тип ( Boolean ), два составных типа: объект ( Object ) и массив ( Array ) и два специальных типа: пустое значение ( Null ) и неопределенное значение ( Undefined ). Тип данных ассоциирован не с именем переменной, а с ее значением.

    Строковые данные – любая цепочка символов, заключенная в одиночные (') или двойные кавычки ("). Одиночные кавычки могут содержаться в строках, заключенных в двойные кавычки и наоборот.

    "простая строка"
    'еще одна строка'
    "don't worry"

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

    Число Десятичное представление Описание
    0.0001, .0001, 1e-4, 1.0e-4 0.0001 Разные представления числа с плавающей запятой
    125 125 Целое
    0377 255 Восьмеричное представление целого
    0378 378 Десятичное целое. Восьмеричное представление допускает лишь цифры от 0 до 7
    0xff 255 Шестнадцатеричное представление
    0x3e7 999 Шестнадцатеричное представление
    00.001 0x0.001 - Ошибка компиляции. Восьмеричные и шестнадцатеричные числа могут быть только целыми

    Данные логического (Boolean) типа принимают лишь одно из двух значений: true (истина) или false (ложь).

    y = (x == 10); //истина, если x равно 10

    Чаще всего логические данные или выражения, возвращающие логические данные, используются в управляющих структурах:

    if (x == 10)
        a = c; // выполняется, если x равно 10
      else
        a = b; // выполняется в противном случае

    В качестве логического может быть использовано любое выражение, например, число, строка, массив или объект, при этом, если значение равно 0, пустой строке, пустому (не содержащему элементов) массиву, пустому объекту ( Null ) или неопределенному значению ( undefined ), то условие оценивается как false (ложь), в противном случае как true (истина). Например, две следующие конструкции эквивалентны.

    if (x != 0)//если x не равен 0
          a = 1;
    
        if (x)
          a = 1;

    Массив представляет собой данные, доступ к элементам которых осуществляется по индексу. Массивы в Javascript создаются следующим образом:

    var a = new Array();// создает новый пустой массив
    var b = new Array(N);//где N – положительное целое. 
    //Создает массив из N пустых элементов
    var c = new Array(elem1, elem2 ...);
    //создает массив из перечисленных элементов. 
    //Элементы должны существовать заранее.
    var d = [elem1, elem2 ...];
    //создает массив из перечисленных элементов
    var e = [];// создает новый пустой массив

    Индексация массивов всегда начинается с нуля (0), то есть последний существующий элемент имеет индекс на 1 меньше длины массива.

    document.write(c[0]); //будет выведен первый элемент массива c
        a[0] = 1; // присвоение значения первому элементу массива a
        a[10] = 10; // присвоение значения одиннадцатому элементу массива a
        // элементы 1 – 9 заполняются пустыми значениями.
        // длина массива a.length равна 11
        document.write(a[10]); // печатает 10
        document.write(a[11]); //печатает пустую строку и не меняет длину массива

    Нет необходимости переопределять длину массива. Присвоение значения несуществующему элементу массива автоматически создает этот элемент (если его не было) и заполняет массив пустыми элементами между последним существовавшим и вновь добавленным элементом.

    Операторы и управляющие структуры

    Javascript поддерживает арифметические, логические и битовые операторы и ряд вспомогательных операторов.

    Арифметические операторы

    Операции сложения (+), вычитания (-), умножения (*) и деления (/) выполняются в общепринятой манере.

    x = ((1 + 2)*3 – 4)/5; // 1

    Оператор деления с остатком (%) делит первое число на второе и возвращает остаток

    a = 9 % 4; // 1
        b = 19 % 6.7; // 5.6

    В Javascript нет операции целочисленного деления. Чтобы получить целое частное, следует отсечь десятичные знаки.

    dividend = 13; // делимое
        divisor = 5; // делитель
        quotient = parseInt(dividend / divisor); // частное (2)
        reminder = dividend % divisor; // остаток (3)

    Оператор плюс (+) требует некоторого дополнительного внимания. Помимо арифметического сложения этот оператор выполняет также конкатенацию (строковое слияние). Результат может иногда оказаться неожиданным (для начинающего разработчика).

    a = 1;
        b = '2';
        c = a + b; // 12

    Строковое слияние происходит всегда, когда хотя бы один из операндов – строка. Остальные арифметические операторы пытаются сначала преобразовать строку в число и только в случае неуспеха возвращают ошибку NaN (Not a Number).

    Арифметические операторы могут быть объединены с оператором присваивания (=), что является удобной сокращенной записью операций.

    a += 3; // эквивалентно a = a + 3
        b /= 2; // b = b / 2
        c = 'Hello';
        c += ' world!'; // Hello world

    Есть также три одноместных оператора: смены знака (-), инкремента (++) и декремента (--).

    x = -x; // смена знака
        x++; // эквивалентно x += 1 или x = x + 1
        --x; // эквивалентно x -= 1 или x = x – 1

    Операторы инкремента и декремента могут иметь префиксную ( ++x ) и постфиксную ( x++ ) формы. В префиксной форме переменная сначала изменяется, затем используется, в постфиксной сначала используется, затем изменяется.

    var a, b, c, d;
        a = b = 2;
        // так можно, поскольку оператор присваивания возвращает значение
        c = ++a; // 3
        d = b++; // 2

    Логические операторы

    Операторы сравнения производят сравнение числовых или строковых операндов и возвращают логическое значение. При сравнении строки рассматриваются как цепочки 16-битных символов. Следует иметь в виду, что в таблицах символов заглавные буквы идут впереди строчных, таким образом "Я" оказывается меньше "автор". Для правильного алфавитного сравнения строки следует привести к верхнему или нижнему регистру.

    Операторы > (больше), < (меньше), >= (больше или равно), <= (меньше или равно):

  • Пытаются привести оба операнда к числам
  • Если оба операнда – строки, выполняют строковое сравнение
  • Если любой из операндов имеет значение NaN (Not a Number), возвращают false (ложь)
  • $$-0 == +0$$
  • $$-\infty$$ < $$-\infty$$ < любое число
  • $$+\infty$$ > $$+\infty$$ > любое число
  • Операторы == (равно), != (не равно):

  • Если типы данных различны, пытаются привести их к строке, числу или логическому типу
  • NaN != NaN
  • -0 == +0
  • null == null, null == undefined
  • 1 == '1', 1 == true, 0 == false, true != 'true'
  • Операторы сравнения на идентичность ( === ) и неидентичность ( !== ). Для идентичности операнды должны быть одного типа и указывать на один объект.

    Логическое отрицание ( !) меняет истину на ложь и наоборот.

    Логическое И ( ) возвращает истину, только когда оба операнда истинны.

    Логическое ИЛИ ( || )возвращает истину, только когда хотя бы один из операндов истина.

    "Тройной" условный оператор позволяет сократить запись при присваивании значения по условию:

    var x = 3;
      result = x % 2 == 0 ? "четное" : "нечетное";
      //эквивалентно
      if (x % 2 == 0)
        result = "четное";
      else
        result = "нечетное";

    Управляющие структуры

    Ветвящиеся структуры, if, if…else и switch, позволяют направить выполнение программы в соответствии с проверяемым условием.

    if (b != 0)
          c = a / b; //выполнить деление только если делитель не ноль
    
        if (x % 2 == 0)
          document.write(x + ' четное');
        else
          document.write(x + ' нечетное');
    
        var d = new Date(); // объект Date – текущая дата и время
        switch (d.getDay()) {
          case 0:
            document.write("Воскресенье");
            break; //выход из переключателя. 
          //Если опущен, будет выполнена и следующая инструкция  
          case 1:
            document.write("Понедельник");
            break;
          case 2:
            document.write("Вторник");
            break;
          case 3:
            document.write("Среда");
            break;
          case 4:
            document.write("Четверг");
            break;
          case 5:
            document.write("Пятница");
            break;
          case 6:
            document.write("Суббота");
            break;
          default: // выполняется, если не выполнено 
            //ни одно из предыдущих условий
            document.write(d.getDay());
        }

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

    Цикл for управляется счетчиком.

    var a = []; // объявляем массив
        for (var i = 0; i < 10; i++)
          a[i] = i; // заполняем 10 элементов массива
        var sum = 0;
        for (var j = 0, N = a.length; j < N; j++)
          sum += a[j];
        document.write('Сумма = ' + sum);

    Специальная форма for, for/in, удобна для перебора свойств объекта.

    var nav = window.navigator; // информация о браузере
        for (prop in nav)
          document.write(prop + ': ' + nav[prop] + '<br>\n');
        // печатает построчно пары свойство: значение объекта.

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

    var x = 0;
        while (x != 4) {//цикл повторяется, пока условие истинно
          x = window.prompt('Сколько будет 2 * 2', x);
          //метод window.prompt открывает диалоговое окно с полем ввода,
          //куда нужно ввести ответ. 
        }
        if (x == 4)
          window.alert("Правильно");

    В цикле do…while условие проверяется в конце цикла, то есть цикл повторяется как минимум один раз.

    var x = 0;
        do {
          x = window.prompt('Сколько будет 2 * 2', x);
        } while (x != 4); //цикл повторяется, пока условие истинно
        if (x == 4)
          window.alert("Правильно");

    В некоторых случаях требуется выход из цикла или переход к началу цикла до завершения всего блока инструкций. Оператор break заканчивает выполнение цикла и передает управление первой инструкции после цикла. Оператор continue пропускает инструкции до конца блока и начинает новый повтор цикла.

    var x = 0;
        while (x != 4) {
          x = window.prompt("Сколько будет 2*2?", x);
    
          if (x == null)//пользователь нажал "Отмена"
            break; // выход из цикла
    
          if (Number(x) == x)// является ли x числом
            continue; // пропускаем остаток и начинаем новый повтор
          // с проверкой условия
    
          //сюда попадаем, только если введено не число
          window.alert("Вводите только числа");
        } // конец цикла
    
        if (x == null)
          window.alert("Вы сдались");
        else
          window.alert("Вы угадали");

    Встроенные объекты Javascript

    Javascript поддерживает ряд собственных объектов, среди которых обратим внимание на следующие:

    Объект Object содержится во всех остальных объектах и обеспечивает их общую функциональность. Уже упомянутые числа, строки и массивы являются объектами Number, String и Array, соответственно, и каждый из этих объектов имеет свой набор свойств и методов. Объект Math открывает доступ к алгебраическим и тригонометрическим функциям. Объект Date предназначен для операций с датой и временем; при работе в браузере доступны лишь функции чтения данных, такие как getDate, getDay, но не setDate или setTime. Объект Global создается при запуске движка Javascript, его свойства, такие как Infinity (бесконечность), NaN (ошибка Not a Number) и undefined (неопределенная переменная), и методы доступны автоматически.

    var a = '3'; //строка
        var b = 4; //число
        var c = a + b; //34 – строковое слияние
        var d = parseFloat(a) + b; //7
        //функция parseFloat – читать как число с плавающей запятой – 
        //гарантирует, что строка, содержащая число, будет интерпретирована
        //как число
    
        var str = 'var x = 2 + 3;';
        eval(str);
        document.write(x); //5
        //функция eval выполняет код, заданный в виде строки, 
        //которая может генерироваться динамически

    Особое место занимает объект Function (функция). Функции в Javascript являются объектами первого класса, то есть могут присваиваться переменным, выступать аргументами других функций и быть результатом (возвращаемым) функций. В Javascript есть несколько вариантов синтаксиса создания функций.

    //классический синтаксис
        function a1(x, y) {
          return x + y;
        }
    
        //явное создание объекта Function
        var a2 = new Function('x', 'y', 'return x + y');
        //обязательным является только последний аргумент – тело функции
    
        //еще один вариант
        var a3 = function(x, y) { return x + y; };

    Независимо от способа задания функции ее вызов осуществляется одинаково и с одинаковым результатом.

    a = a1(3, 4);
        b = a2(3, 4);
        c = a3(3, 4);

    Функция как объект имеет собственные свойства

    function ArgTest(a, b) {
        var i, s = "Функция ArgTest ожидает ";
        //число аргументов при объявлении функции
        var expargs = ArgTest.length;
    
        //число аргументов при вызове функции
        var numargs = ArgTest.arguments.length;
        if (expargs < 2)
          s += expargs + " аргумент. ";
        else
          s += expargs + " аргумента. ";
        if (numargs == 1)
          s += " Получен 1 аргумент.";
        else
          s += "Получено " + numargs + " аргументов.";
        return (s);
      }

    Массив arguments содержит аргументы, отправленные в функцию при вызове, даже если они не были перечислены при объявлении функции.

    function sum() {
          var s = 0;
          for (var i = 0, n = arguments.length; i < n; i++)
            s += arguments[i];
          return s;
        }
        document.write(sum(1, 2, 3, 4)); //10

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

    //создаем и заполняем массив 10 элементами
      var arr = [];
      for (var i = 0; i < 10; i++)
        arr[i] = i;
    
      //сортируем массив по убыванию
      arr.sort(function(x, y) { return x < y ? 1 : -1; });
      //функция сравнения принимает два аргумента и возвращает -1, 
      //если первый элемент должен идти впереди второго, 
      //и 1 в противном случае
    
      //перетасовываем массив, т.е. сортируем в произвольном порядке
      arr.sort(function() { return Math.random() < 0.5 ? 1 : -1; });
      //от аргументов функции сравнения ничего не зависит, 
      //поэтому они могут быть опущены. 1 и -1 возвращаются 
      //в случайном порядке

    Функции могут вызываться из функций, в том числе из самих себя (рекурсия). Классический пример рекурсии – расчет факториала.

    function Factorial(N) {
          return N < 2 ? 1 : N * Factorial(N - 1);
        }

    Создание пользовательских объектов

    Функции могут служить конструкторами пользовательских объектов. В этом случае ключевое слово this является ссылкой на вновь создаваемый объект.

    //конструктор объекта Окружность
      function Circle(x, y, r) {
        this.x = x; //x-координата центра
        this.y = y; //y-координата центра
        this.r = r; //радиус окружности
      }
    
      //создание объектов
      var circle1 = new Circle(1, 1, 2);
      var circle2 = new Circle(3, 3, 1);

    Свойство prototype функции-конструктора позволяет добавлять свойства и методы как к пользовательским, так и встроенным объектам.

    function getLength() {
          //this является ссылкой на объект, 
          //в контексте которого осуществляется вызов
          return 2 * Math.PI * this.r; //длина окружности
        }
    
        //подключаем функцию к конструктору
        //обратите внимание на отсутствие скобок 
        //без скобок присваивается объект-функция
        //со скобками – результат выполнения функции
        Circle.prototype.length = getLength;
    
        //Добавим еще один метод
        Circle.prototype.area = function() {
          return Math.PI * this.r * this.r; //площадь круга
        }
    
        document.write("circle1 имеет радиус " + circle1.r +
          ", длину " + circle1.length() +
          " и площадь " + circle1.area());

    Использование функции-конструктора позволяют создавать любое число объектов. Если требуется всего один объект, можно воспользоваться альтернативным синтаксисом.

    var uniqueObj = {
          prop1: 10, //определяем числовое свойство
          prop2: 'red', //определяем строковое свойство
          method1: function() { return 'Hello'; } //определяем метод
        };

    Внешние объекты

    Internet Explorer и другие браузеры открывают доступ к собственной объектной модели через программный интерфейс приложения (Application Programming Interface, API), так что свойства, методы и события объектов браузера становятся доступны для Javascript.

    Пример: карточная игра Blackjack

    В качестве примера разработаем карточную игру, известную под названием Blackjack, которая будет работать в веб-браузере.

    Правила игры

    Игра ведется колодой карт из 52 листов, от двойки до туза в каждой из четырех мастей. Каждая карта имеет свою цену, в очках. (От двойки до десятки в соответствии со значением, картинки – 10, туз 11 или 1, в зависимости от набранных очков)

    В игре участвуют два игрока: собственно игрок и крупье. Цель игры - набрать больше очков, чем противник, но не более 21 (за исключением двух тузов).

    Игрок (человек) делает ставку в пределах своего баланса и берет карты по одной, пока не сочтет количество очков достаточным, после чего передает ход крупье.

    Крупье (компьютер) действует автоматически.

    Разработка объектной модели

    Колода состоит из карт. Каждая карта характеризуется мастью, значением и числом очков. Кроме того, уместно привязать к карте ее картинку. Фактически, если перенумеровать все карты в колоде от 0 до 51, то все свойства карты можно рассчитать из ее порядкового номера.

    //конструктор карты
        function Card(n) {
          //вспомогательные массивы
          var suits = ['треф', 'бубен', 'червей', 'пик'];
          var names = ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'Валет', 'Дама', 'Король', 'Туз'];
          var values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 11];
    
          //рассчитываем свойства карты по ее номеру
          this.suit = suits[parseInt(n / 13)]; //масть
          this.name = names[n % 13]; //название
          this.value = values[n % 13]; //очки
    
          //прикладываем картинку к карте - объект Image Веб браузера
          this.img = new Image();
          //рассчитываем имя файла и инициализируем загрузку файла,
          //полагая, что изображения хранятся в папке deck
          //и поименованы от 0.gif для двойки треф
          //до 51.gif для туза пик
          this.img.src = 'deck/' + n + '.gif';
          //задаем дополнительные атрибуты изображения
          this.img.alt = this.toString();
          this.img.className = 'Card';
        }
        // переопределяем функцию toString
        Card.prototype.toString = function() {
          return this.name + ' ' + this.suit;
        }

    Колода – единичный объект. Создадим его для дальнейшего использования.

    var Deck = {
          cards: [], //собственно колода - массив карт, изначально пустой
          num: 0, // внутренний счетчик
    
          //колоду нужно инициализировать перед первым использованием
          init: function() {
            //заполняем массив
            for (var i = 0; i < 52; i++)
            //все данные карты рассчитываются по ее номеру
              this.cards[i] = new Card(i);
          },
    
          //тасуем колоду, т.е. сортируем в случайном порядке
          shuffle: function() {
            this.cards.sort(function() {
                return (Math.random() < 0.5 ? 1 : -1);
              }
            );
            this.num = 0; //инициализируем счетчик
          },
    
          nextCard: function() {
            //возвращает верхнюю карту из колоды
            return this.cards[this.num++];
          }
        };

    В игре участвуют двое. Создадим конструктор для них.

    function Hand() { } // конструктор игрока необходим, даже пустой
    
        Hand.prototype = {//свойства и методы игрока
          balance: 1000, //начальная сумма
          currentBet: 0, //текущая ставка
          cards: [], //массив карт у игрока
          //Методы игрока
          init: function() {//сбросить карты
            this.cards = new Array();
          },
          bet: function() {//увеличиваем ставку
            this.balance += this.currentBet;
            if (this.currentBet == 0)
              this.currentBet = 10; //произвольная начальная ставка
            else
              this.currentBet *= 2; //удваиваем ставку
    
            //ограничим ставку балансом игрока
            if (this.balance >= this.currentBet)
              this.balance -= this.currentBet;
            else {
              this.currentBet = this.balance;
              this.balance = 0
            }
            //возвращаем значение для удобства использования
            return this.currentBet;
          },
          nextCard: function() {//взять следующую карту
            var card = Deck.nextCard(); //следующая карта из колоды
            this.cards.push(card); //добавляем в конец массива
            //возвращаем значение для удобства использования
            return card;
          },
          //подсчитываем очки на руке игрока
          calc: function() {
            var sum = 0; //сумма очков
            var aces = 0; // число тузов
            if (this.cards)//если массив карт не пустой
              for (var i in this.cards) {
              if (this.cards[i].value == 11)
                aces++; //найден туз
              //суммируем очки
              sum += this.cards[i].value;
            }
            //если на руке не более 2 карт, пересчет не нужен
            if (this.cards.length < 3)
              return sum;
            // в случае перебора считаем туза за 1
            while (sum > 21  aces > 0) {
              sum -= 10;
              aces--;
            }
            return sum;
          },
          //сравнение двух игроков
          compare: function(anotherHand) {
            //результат: 1 - победил первый, 
            //-1 - победил второй, 0 – ровно
            //подсчитываем очки первого игрока
            var a = this.calc();
            if (a > 21  this.cards.length > 2)//перебор у первого
              return -1;
            //подсчитываем очки второго игрока
            var b = anotherHand.calc();
            if (b > 21  anotherHand.cards.length > 2)
            // перебор у второго
              return 1;
            if (a == b)//ровно
              return 0;
            if (a == 22  this.cards.length == 2)
            // у первого игрока Black Jack
              return 1;
            if (b == 22  anotherHand.cards.length == 2)
            // у второго игрока Black Jack
              return -1;
            if (a > b)//У первого больше
              return 1;
            //остается единственный вариант - у второго больше
            return -1;
          }
        };

    Объектная модель создана, и мы можем ее использовать. Сохраним весь вышеприведенный код в файле blackjack.js. Создадим HTML-документ ( blackjack.htm ):

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
      <title>BlackJack</title>
    
      <script type="text/javascript" src="blackjack.js"></script>
    
      <link href="blackjack.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
      <!-- Весь пользовательский интерфейс располагается здесь -->
    
      <div class="Hand" id="dealer">
      </div>
    
      <div id="result">
      </div>
    
      <div id="player" class="Hand">
      </div>
    
      <div>
        Баланс: $<span id="balance"></span> Ставка: $<span id="bet"></span><br />
        Очки: <span id="sum"></span>
      </div>
    
      <input type="button" onclick="bet()" value="увеличить ставку" id="btnBet" />
      <input type="button" onclick="nextCard()" value="взять карту" id="btnRun" />
      <input type="button" onclick="finish()" value="Крупье" id="btnFinish" />
    </body>
    </html>

    Стилевая информация хранится в отдельном файле blackjack.css. Вот примерное содержание:

    body
    {
        background-color: Green;
        color: White;
        font-family: Comic Sans MS;
        font-size: 14px;
        text-align: center;
    }
    .Card
    {
        border: solid 1px #000;
        margin: 6px;
    }
    .Hand
    {
        height: 120px;/* Делаем достаточно высоким, чтобы поместилась карта */
    }
    #result
    {
        height: 30px;
    }

    Остается создать игроков, инициализировать колоду карт и обработать события нажатия на кнопки.

    //задаем функцию, выполняемую при загрузке страницы
        window.onload = function() {
          Deck.init(); //заполняем колоду картами
          player = new Hand(); //игрок объявлен глобально
          dealer = new Hand(); //крупье объявлен глобально
    
          //кнопки "Взять карту" и "крупье" изначально недоступны
          document.getElementById('btnRun').disabled = true;
          document.getElementById('btnFinish').disabled = true;
          //вспомогательная функция Show() определена ниже
          Show();
        }
        //обрабатываем нажатие кнопки "увеличить ставку"
        function bet() {
          dealer.init(); //сбросить карты у крупье
          player.init(); //сбросить карты у игрока
          Deck.shuffle(); //перетасовать колоду
          //очищаем окно от карт и результата
          document.getElementById('player').innerHTML = '';
          document.getElementById('dealer').innerHTML = '';
          document.getElementById('result').innerHTML = '';
          document.getElementById('sum').innerHTML = '';
          if (player.bet()) {
            // нет необходимости писать if (player.bet() > 0)
            // 0 == false, любое другое число == true
            document.getElementById('btnRun').disabled = false;
          }
          Show();
        }
        //обрабатываем нажатие кнопки "взять карту"
        function nextCard() {
          var oTest = document.getElementById('player');
          var oSum = document.getElementById('sum');
    
          //берем карту и показываем ее в окне браузера
          oTest.appendChild(player.nextCard().img);
          var s = player.calc(); //подсчитываем очки
          oSum.innerHTML = s; //показываем очки
          //делаем доступной карту "Крупье"
          document.getElementById('btnFinish').disabled = false;
          if (s > 21  player.cards.length > 2) {
            //у игрока перебор. нет смысла продолжать
            document.getElementById('result').innerHTML = 'Не повезло';
            player.currentBet = 0;
            document.getElementById('btnFinish').disabled = true;
            document.getElementById('btnRun').disabled = true;
          }
          Show();
        }
        //обрабатываем нажатие кнопки "крупье"
        function finish() {
          document.getElementById('btnRun').disabled = true;
          document.getElementById('btnFinish').disabled = true;
          //находим элемент с id="dealer"
          var oDealer = document.getElementById('dealer');
          //таково внутреннее правило казино - рисковать до 16 очков
          while (dealer.calc() < 17) {
            //добавляем карты крупье и отображаем их
            oDealer.appendChild(dealer.nextCard().img);
          }
          switch (player.compare(dealer)) {
            case 1:
              document.getElementById('result').innerHTML = '!!! Победа !!!';
              player.balance += player.currentBet * 2;
              player.currentBet = 0;
              break;
            case -1:
              document.getElementById('result').innerHTML = 'Не повезло';
              player.currentBet = 0;
              break;
            default: // 0 - ровно
              document.getElementById('result').innerHTML = '??? Ровно ???';
              break;
          }
          Show();
        }
        //вспомогательная функция
        function Show() {
          //находим элемент с id='balance' и показываем баланс игрока
          //у крупье баланс не ограничен
          document.getElementById('balance').innerHTML = player.balance;
          document.getElementById('bet').innerHTML = player.currentBet;
        }

    Этот код можно добавить в файл blackjack.js. Сохраните все файлы в одной папке. Откройте файл blackjack.htm в браузере.

    Вопросы

  • Какое назначение имеет язык Javascript?
  • Каким образом сценарий Javascript вставляется в HTML-документ?
  • Как объявляется переменная? Какие имена недопустимы?
  • Какие типы данных имеются в Javascript?
  • Как создаётся массив? С чего начинается нумерация элементов массива?
  • Перечислите и охарактеризуйте управляющие структуры Javascript.
  • Какие существуют в Javascript варианты синтаксиса создания функций?
  • Как создать несколько однотипных пользовательских объектов?
  • Как добавить метод к типу объекта?
  • Как создаётся объект при отсутствии необходимости определения его типа?
  • Вернуться к учебному плану