Опубликован: 01.07.2011 | Уровень: для всех | Доступ: платный
Лекция 7:

Объекты в JavaScript

< Лекция 6 || Лекция 7 || Лекция 8 >
Аннотация: Причины внимания к объектам. Создание объектов. Ссылка на себя. Объекты как ассоциативные массивы. Объектный литерал.

Введение

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

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

Лекция имеет следующую структуру:

  • Зачем нужны объекты?
  • Знакомая территория
  • Создание объектов
  • Ссылка на себя
  • Объекты как ассоциативные массивы
  • Объектный литерал
  • Заключение - еще много надо изучить
  • Дополнительное чтение
  • Контрольные вопросы

Примечание: Существует пример, доступный для скачивания и выполнения, который содержит код вычисления площади треугольника, как с объектами, так и без них. Этот код создается ниже в ходе изложения лекции. Выполните пример с объектами треугольниками (http://dev.opera.com/articles/view/objects-in-javascript/triangle_area.html).

Зачем нужны объекты?

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

Вы знаете, что треугольник имеет три стороны, поэтому для работы с определенным треугольником, очевидно, необходимо создать три переменные:

// Это треугольник.
var sideA = 3;
var sideB = 4;
var sideC = 5;

И вот мы получили треугольник! Но не совсем треугольник, не так ли? На самом деле мы просто создали три переменные, которые необходимо отслеживать по отдельности, и комментарий, чтобы напомнить себе, что имеется в виду. Это просто не так уж ясно и не так уж удобно использовать, как могло бы быть. Но неважно, давайте продолжим и рассмотрим, как можно выполнить какие-нибудь вычисления с таким "треугольником". Чтобы найти его площадь, необходимо написать функцию следующего вида:

function getArea( a, b, c ) {
  // Возвращает площадь треугольника, используя формулу Герона 
        
  var semiperimeter   =   (a + b + c) / 2;
  var calculation     =   semiperimeter * (semiperimeter - a) * (semiperimeter - b) * (semiperimeter - c);
  return Math.sqrt( calculation );
}

alert( getArea( sideA, sideB, sideC ) );

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

Более того, я использовал подходящее общее имя для функции и каждой из переменных: getArea, sideA, и т.д. Что произойдет, если на следующей неделе обнаружится, что требуется расширить программу, чтобы включить прямоугольник? Я бы хотел использовать переменные sideA и sideB для сторон прямоугольника, но эти имена переменных уже заняты. Я мог бы использовать side1 и side2, но я готов поспорить, что вы можете увидеть, почему это рецепт для создания путаницы и бедствия. Вероятно, я бы закончил выбирая rectangleSideA и rectangleSideB, и, чтобы оставаться последовательным, я бы также должен был вернуться назад и изменить весь уже написанный код для треугольников, чтобы использовать triangleSideA и т.д., что создает некоторые возможности для ошибок. То же самое происходит с именем функции. Я бы хотел использовать getArea для обеих фигур, так как это концептуально одни и те же вычисления, но я не могу. Должен быть лучший способ для представления моих данных!

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

Знакомая территория

Если взглянуть на последний пример с функциями из предыдущей лекции, можно увидеть фрагменты кода следующего вида:

var obj = document.getElementById( elementID );

и

obj.style.background = 'rgb('+red+','+green+','+blue')';

Вы удивлены! Мы использовали объекты, даже не зная об этом! Давайте рассмотрим подробнее два эти фрагмента, чтобы начать изучение синтаксиса объектов JavaScript.

Код var obj = document.getElementById( elementID ) должен выглядеть в некоторой степени знакомым. Вы знаете, что скобки в конце команды означают, что выполняется некоторая функция, и можно видеть, что результат вызова функции сохраняется в переменной с именем obj. Единственным новым элементом здесь является точка в середине. Оказывается, что эта запись с точкой используется в JavaScript для предоставления доступа к данным внутри объекта. Точка (.) является просто оператором, который помещается между его операндами, также как + и -.

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

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

Следующий фрагмент кода немного интереснее: он содержит две точки. Одним из действительно привлекательных моментов поддержки объектов в JavaScript является понятие сцепления точек, чтобы погрузиться внутрь комплексных структур. Короче говоря, можно соединять объекты цепочкой таким же образом, как можно выполнить var x = 2 + 3 + 4 + 5 и ожидать в результате 14; ссылки на объекты просто разрешают себя слева направо (точным научным языком это означает, что оператор точки в JavaScript является "лево- ассоциативным инфиксным оператором"). В данном случае вычисляется obj.style, разрешаясь в объект, после чего происходит обращение к его свойству background. При желании можно сделать это в коде явно, добавляя скобки: (obj.style).background.

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

Чтобы создать собственный объект треугольника, мы создадим его явно, используя следующий синтаксис:

var triangle = new Object();

triangle является теперь пустой основой, ожидающей создания некоторой конструкции с тремя сторонами. Можно сделать это, добавляя в объект свойства с помощью оператора точки:

triangle.sideA  =   3;
triangle.sideB  =   4;
triangle.sideC  =   5;

На самом деле ничего специального для добавления новых свойств в объект делать не требуется. Когда JavaScript вычисляет оператор точки, он на самом деле действует достаточно снисходительно. Если вы попытаетесь задать свойство, которое не существует, JavaScript создаст это свойство. Если вы попытаетесь прочитать свойство, которого там нет, JavaScript вернет "undefined" ("не определено"). Это удобно, но может скрывать при неосторожном обращении ошибки, поэтому следите за опечатками!

Добавление методов работает аналогично - вот пример:

triangle.getArea    =   function ( a, b, c ) {
  // Возвращает область треугольника, используя формулу Герона
        
  var semiperimeter   =   (a + b + c) / 2;
  var calculation     =   semiperimeter * (semiperimeter - a) *
                                (semiperimeter - b) * (semiperimeter - c);
  return Math.sqrt( calculation );
        
};  // Обратите внимание на точку с запятой в этом месте; она 
         //   обязательна.

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

Ссылка на себя

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

triangle.getArea(triangle.sideA, triangle.sideB, triangle.sideC);

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

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

triangle.getArea    =   function () {
  // Возвращает площадь треугольника, используя формулу Герона 
  var semiperimeter   =   (this.sideA + this.sideB + this.sideC) / 2;
  var calculation     =   semiperimeter * (semiperimeter - this.sideA) * (semiperimeter - this.sideB) *
   (semiperimeter - this.sideC);
  return Math.sqrt( calculation );
};      // Обратите внимание здесь на точку с запятой, она обязательна.

Как можно видеть, это работает в некоторой степени как зеркало. Когда выполняется метод getArea, он посмотрит на свое отражение, чтобы считать свои свойства sideA, sideB, и sideC. Он может использовать это в вычислениях, вместо использования внешнего ввода.

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

Объекты как ассоциативные массивы

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

var triangle = new Object();
triangle['sideA']   =   3;
triangle['sideB']   =   4;
triangle['sideC']   =   5;
triangle['getArea'] =   function ( a, b, c ) {
  // возвращает площадь треугольника, используя формулу Герона 
        
  var semiperimeter   =   (a + b + c) / 2;
  var calculation     =   semiperimeter * (semiperimeter - a) * (semiperimeter - b) * (semiperimeter - c);
  return Math.sqrt( calculation );
        
}; // Обратите внимание на точку с запятой, она здесь обязательна

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

function isPropertyShared( objectA, objectB, propertyName ) {
  if (
     typeof objectA[ propertyName ] !== undefined
     &&
     typeof objectB[ propertyName ] !== undefined
     ) {
         alert("Оба объекта имеют свойство с именем  " + propertyName + "!");
       }
}

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

Примечание: Ассоциативный массив называется "hash" в Perl, "hashtable" в C#, "map" в C++, "hashmap" в Java, "dictionary" в Python, и т.д. Это очень мощная и фундаментальная концепция в программировании, и вы вполне можете быть с ней знакомы под другим именем.

Объектный литерал

Давайте внимательнее посмотрим на код, который, кажется, вполне знакомым:

alert("Hello world");

Можно сразу определить alert как функцию, которая вызывается с одним аргументом: строкой "Hello world". Я хотел бы обратить здесь внимание на то, что не требуется писать:

var temporaryString = "Hello world";
alert(temporaryString);

JavaScript просто понимает, что все, содержащееся внутри пары двойных кавычек (" "), должно интерпретироваться как строка, и делает всю необходимую фоновую работу, необходимую для правильной работы такой записи. Создается строка и передается прямо в функцию. Формально "Hello world" называется строковым литералом ; вы, чтобы создать строку, просто вводите все, что должно в ней находиться.

JavaScript имеет аналогичный синтаксис для "объектных литералов ", который позволяет создавать свои собственные объекты без каких-либо синтаксических накладных расходов. Давайте перепишем созданный выше объект еще третьим способом, теперь как объектный литерал.

var triangle = {
  sideA:      3,
  sideB:      4,
  sideC:      5,
  getArea:    function ( a, b, c ) {
    // Возвращает площадь треугольника, используя формулу Герона 
        
    var semiperimeter   =   (a + b + c) / 2;
    var calculation     =   semiperimeter * (semiperimeter - a) * (semiperimeter - b) * (semiperimeter - c);
    return Math.sqrt( calculation );
        
  }
};

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

Однако надо следить за одной вещью: достаточно распространенная ошибка состоит в размещении запятой после последнего элемента в списке свойств объектного литерала (в данном случае после определения getArea ). Размещайте запятую только между свойствами - дополнительная запятая в конце будет вызывать ошибки. Особенно при повторном обращении к коду для добавления или удаления значений, нужно быть очень осторожным, чтобы сохранить запятые на правильных местах.

Заключение - еще много надо изучить

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

Дополнительное чтение

Контрольные вопросы

  • Когда вы можете захотеть использовать запись с индексом вместо записи с точкой при указании свойств объекта?
  • Как объект может ссылаться на самого себя? Почему это полезно?
  • Что такое объектный литерал? При создании объектного литерала, где располагается запятая?
  • Я создал объект для представления треугольника, и вычислил его площадь. Сделайте то же самое для прямоугольника. Используйте this в методе getArea, чтобы избежать ненужной передачи данных.

Об авторе


Майк Вест является студентом философии, который ловко маскируется под опытного и успешного web-разработчика. Он работает с web более десяти лет, в последнее время в команде, которая отвечает за создание европейских новостных сайтов Yahoo!.

После того как он покинул в 2005 году широкие пригородные равнины Техаса, Майк поселился в Мюнхене, Германия, где он сражается с языком каждый день все в меньшей степени. mikewest.org (http://mikewest.org/) является его домом в web, собирающим (понемногу) его письменные творения и ссылки для потомства. Он хранит свой код на сайте GitHub (http://github.com/mikewest).

< Лекция 6 || Лекция 7 || Лекция 8 >
Сергей Крупко
Сергей Крупко

Добрый день.

Я сейчас прохожу курс  повышения квалификации  - "Профессиональное веб-программирование". Мне нужно получить диплом по этому курсу. Я так полагаю нужно его оплатить чтобы получить диплом о повышении квалификации. Как мне оплатить этот курс?

 

Галина Башкирова
Галина Башкирова

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

Системный администратор информационно-коммуникационных» систем.
Мне нужно самой найти тему? или делать по высланным темам

 

Фёдор Лосенко
Фёдор Лосенко
Россия
Александр Некрасов
Александр Некрасов
Россия, Москва