Введение в объектно-ориентированный JavaScript

ОГЛАВЛЕНИЕ

Введение в написание объектно- ориентированных структур на JavaScript

•    Скачать примеры - 7.04 Кб

Оглавление

  • Введение
    • Предпосылки
    • Основные понятия
  • Простой класс на JavaScript
    • Определение класса
    • Создание свойств класса
    • Создание методов класса
  • Инкапсуляция
    • Открытые, защищенные и закрытые члены
    • Инкапсуляция на практике
    • Вывод по инкапсуляции
  • Наследование
    • Наследование свойств
    • Наследование методов
    • Созданиеэкземпляраунаследованного класса
    • Выводпо наследованию
  • Полиморфизм
    • Вывод по полиморфизму
  • Вывод

Введение

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

Объектно-ориентированный JavaScript имеет несколько преимуществ. Так как это интерпретируемый язык, то методы и свойства могут добавляться к классу в любое время и не обязаны быть объявленными в конструкторе класса, как в других объектно-ориентированных языках, таких как C++. Так как JavaScript поддерживает переменные типы данных, свойства класса не обязательно должны иметь фиксированный тип данных (такой как Boolean или string) и могут меняться в любое время. Более того, объектно-ориентированный JavaScript более гибок и эффективен, чем процедурный JavaScript, так как объекты полностью поддерживают инкапсуляцию и наследование, и полиморфизм реализуется с помощью свойства prototype.

Предпосылки

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

Основные понятия

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

•    Класс: Определение объекта, включающее его методы и свойства.
•    Инкапсуляция: Когда данные, передаваемые внутри экземпляра объекта, остаются заключенными внутри экземпляра этого объекта. При создании нового экземпляра объекта для него создается новый набор данных.
•    Наследование: Когда объект становится "дочерним" объектом или подклассом другого класса, и свойства и методы родительского класса применяются к подклассу.
•    Полиморфизм: Когда подкласс класса может вызвать ту же самую обобщенную унаследованную функцию в своем собственном контексте.
•    Свойство: Переменная, относящаяся к классу.
•    Метод: Функция, относящаяся к классу.

Простой класс на JavaScript

Определение класса

Типовой класс очень легко реализуется на JavaScript. Чтобы определить класс, надо лишь объявить function(функцию):

<script language="Javascript">
..

function MyClass()
{
}

..
</script>

Эти три строки кода создают новый объект по имени MyClass, экземпляры которого создаются с помощью оператора new(новый), например:

var c = new MyClass();

Функция MyClass также служит конструктором класса, и когда новый экземпляр этого класса вызывается с оператором new, вызывается данная функция.

Создание свойств класса

Пока этот код является лишь простым классом с одним объявленным конструктором. Для добавления свойств к классу используется оператор this, за которым следует имя свойства. Как отмечалось ранее, методы и свойства могут создаваться везде в JavaScript, а не только в конструкторе класса. Ниже приведен пример добавления свойств к MyClass.

..

function MyClass()
{
  this.MyData = "Some Text";
  this.MoreData = "Some More Text";
}

..

Обратиться к этим свойствам можно так:

var c = new MyClass();
alert(c.MyData);

Данный фрагмент кода добавляет свойства MyData и MoreData к классу. К ним можно обратиться везде внутри конструктора класса и методов класса с помощью оператора this, поэтому к MyData можно обратиться с помощью this.MyData. Также имейте в виду, что в отличие от некоторых объектно-ориентированных языков, к свойствам класса обращаются с помощью ., а не ->. Причина состоит в том, что JavaScript не различает указатели и переменные. Если ссылка на класс сохраняется в переменную при создании, то к свойствам класса можно обращаться по имени переменной, за которой следует., затем имя свойства класса, в данном примере myData, к которому обращаются с помощью c.MyData.

Создание методов класса

Как сказано выше в статье, методы класса создаются с помощью свойства prototype. Когда метод создается в классе в JavaScript, метод добавляется к объекту класса с помощью свойства prototype, как показано в следующем фрагменте кода:

..

function MyClass()
{
  //Любые свойства создаются здесь
}

MyClass.prototype.MyFunction = function()
{
  //Код функции здесь
}

..

Для ясности метод здесь создается с помощью = function(). Помимо этого, метод может быть создан путем объявления function MyClass.prototype.MyFunction(). Этот код делает MyFunction методом MyClass с помощью свойства prototype. Это затем дает MyFunction доступ к любым другим методам или свойствам, созданным в MyClass с помощью оператора this operator. Например:

..

function MyClass()
{
  this.MyData = "Some Text";
}

MyClass.prototype.MyFunction = function(newtext)
{
  this.MyData = newtext;
 
  alert("New text:\n"+this.MyData);
}

..

Этот кусок кода создает класс MyClass, затем создает свойство по имени MyData в конструкторе класса. Метод по имени MyFunction затем добавляется к объекту MyClass с помощью оператора prototype, чтобы он мог обращаться к свойствам и методам MyClass. В этом методе MyData меняется на newtext, являющийся единственным аргументом метода. Это новое значение затем отображается с помощью окна предупреждения.


Инкапсуляция

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

Открытые, защищенные и закрытые члены

Инкапсуляция реализуется в JavaScript путем отделения данных экземпляра внутри класса. Однако нет разных степеней инкапсуляции посредством операторов public, protected и private. Отсюда следует, что доступ к данным нельзя ограничить, как в других объектно-ориентированных языках программирования. Причина в том, что в JavaScript так делать просто не нужно для весьма крупных проектов. К свойствам и методам класса можно обращаться откуда угодно, внутри класса или вне его.

Инкапсуляция на практике

Пример инкапсуляции показан ниже:

..

function MyClass()
{
  this.MyData = "Some Text";
}

MyClass.prototype.MyFunction = function(newtext)
{
  this.MyData = newtext;
 
  alert("New text:\n"+this.MyData);
}

..

var c = new MyClass();
c.MyFunction("Some More Text");

var c2 = new MyClass();
c2.MyFunction("Some Different Text");

При вызове c.MyData возвращает "Some More Text", а c2.MyData возвращает "Some Different Text", показывая, что данные инкапсулированы внутри класса.

Вывод по инкапсуляции

Инкапсуляция – важная часть объектно-ориентированного программирования, чтобы данные в разных экземплярах класса были отделены друг от друга; это реализуется в JavaScript с помощью оператора this. Однако, в отличие от других объектно-ориентированных языков программирования, JavaScript не ограничивает доступ к данным внутри экземпляра класса.

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

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

Как сказано выше в статье, в JavaScript нет прямого наследования, так как он является языком прототипов. Поэтому для наследования класса от другого класса используется оператор prototype, чтобы клонировать конструктор родительского класса и при этом унаследовать его методы и свойства. Конструктор родительского класса также вызывается в конструкторе подкласса, чтобы применить все его методы и свойства к подклассу, как показано в коде ниже:

..

//Конструктор родительского класса
function Animal(name)
{
  this.name = name;
}

//Конструктор унаследованного класса
function Dog(name)
{
  Animal.call(this, name);
}
Dog.prototype = new Animal();

Dog.prototype.ChangeName(newname)
{
  this.name = newname;
}

..

В примере кода выше создаются два класса — базовый класс по имени Animal(животное) и подкласс по имени Dog(собака), унаследованный от Animal. В конструкторе базового класса создается свойство по имени name, которому присваивается переданное ему значение.

При создании унаследованного класса нужны две строки кода для наследования от базового класса, как показано для Dog:

Animal.call(this, name);

Эта строка кода вызывается из конструктора подкласса. call() - функция JavaScript, вызывающая функцию в заданном контексте (первый аргумент). Аргументы, необходимые вызываемой функции, также передаются, начиная со второго аргумента call(), как видно для name. Это означает, что конструктор базового класса вызывается из конструктора подкласса, тем самым применяя к подклассу методы и свойства, созданные в Animal.

Вторая строка кода, необходимая для наследования от базового класса, следующая:

Dog.prototype = new Animal();

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

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

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

Как свойства, методы могут наследоваться от родительского класса в JavaScript, аналогично наследованию свойств, как показано ниже:

..

//Конструктор родительского класса
function Animal(name)
{
  this.name = name;
}

//Метод родительского класса
Animal.prototype.alertName = function()
{
  alert(this.name);
}

//Конструктор унаследованного класса
function Dog(name)
{
  Animal.call(this, name);
 
  this.collarText;
}
Dog.prototype = new Animal();

Dog.prototype.setCollarText = function(text)
{
  this.collarText = text;
}

..

Унаследованный метод вызывается так:

var d = new Dog("Fido");
d.alertName();

Это вызывает alertName(), являющийся унаследованным методом Animal.

Создание экземпляра унаследованного класса

Классы, унаследованные от другого класса, могут вызываться в JavaScript, как вызывался бы базовый класс, и методы и свойства могут вызываться аналогично.

var d = new Dog("Fido");      //Создает экземпляр подкласса
alert(d.name);                //Извлекает данные, унаследованные от родительского //класса
d.setCollarText("FIDO");      //Вызывает метод подкласса

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

var d = new Dog("Fido");      // Создает экземпляр подкласса
d.alertName();                //Вызывает унаследованный метод

Вывод по наследованию

Наследование – один из трех важных принципов объектно-ориентированного программирования. Он реализуется в JavaScript с помощью prototype и функции call().

Полиморфизм

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

..

//Конструктор родительского класса
function Animal(name)
{
  this.name = name;
}

Animal.prototype.speak = function()
{
  alert(this.name + " says:");
}

//Конструктор унаследованного класса "Dog"
function Dog(name)
{
  Animal.call(this, name);
}

Dog.prototype.speak = function()
{
  Animal.prototype.speak.call(this);
 
  alert("woof");
}

//Конструктор унаследованного класса "Cat"
function Cat(name)
{
  Animal.call(this, name);
}

Cat.prototype.speak = function()
{
  Animal.prototype.speak.call(this);
 
  alert("miaow");
}

..

Этот код означает, что если вызывается экземпляр Dog, а затем вызывается функция speak() класса Dog, то она переопределит функцию speak() родительского класса. Однако, хотя мы хотим делать нечто разное с версией speak() каждого подкласса, мы хотим вызывать обобщенную функцию speak() родительского класса с помощью Animal.prototype.speak.call(this);, который вызывает унаследованную функцию в контексте подкласса. Затем, после того как мы делаем что-то еще с ней, что для Dog является alert("woof"); а для Cat является alert("miaow");

При вызове это выглядело бы так:

var d = new Dog("Fido");     //Создает экземпляр Dog
d.speak();                   //Вызывает функцию speak() класса Dog
 
var c = new Cat("Lucy");     //Создает экземпляр Cat
c.speak();                   //Вызывает функцию speak() класса Cat

Первые две строки уведомляют "Fido говорит:" (функция speak() родительского класса), далее следует "woof" (функция speak() класса Dog).

Следующие две строки уведомляют "Lucy говорит:" (функция speak() родительского класса), далее следует "miaow" (функция speak() класса Cat).

Вывод по полиморфизму

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

Вывод

Прочитав данную статью, вы должны уметь:
•    Создавать классы с методами и свойствами
•    Создавать унаследованные классы
•    Создавать полиморфные функции

Это лишь вводная статья, но надеемся, что вы сможете использовать приобретенные навыки для более сложных объектно-ориентированных структур.