Классы в библиотеке PrototypeПрограммистам, привыкшим к синтаксису С++, Java или любого другого объектно-ориентированного языка, синтаксис для описания прототипов и конструкторов в JavaScript может показаться слегка громоздким. С библиотекой Prototype на борту его можно немного приблизить к привычному виду:
var Greeter = Class. create(); Greeter. prototype = { initialize: function(name) { this. name = name; },
greet: function() { alert(‘Hello, ‘ + this. name); } };
В первой строчке с помощью функции Class. create() создается конструктор класса. Этот конструктор одинаковый для всех классов: он вызывает у объекта метод initialize с параметрами, переданными в конструктор. Сделано это для того, чтобы фактический конструктор определялся в прототипе вместе с остальными методами – это более логично и аккуратнее синтаксически.
Во второй строке и далее идет описание прототипа класса. Прототип описывается с помощью JSON, так как такой стиль более близок к привычным Java или C++.
Наследование JavaScript напрямую не предоставляет возможностей для реализации наследования, но мы можем в определенной мере реализовать его самостоятельно. По-сути, наследование в терминах JavaScript – это копирование прототипа базового объекта в прототип дочернего с замещением переопределяемых атрибутов (методов). Для такой операции в Prototype существует специальная функция Object. extend(). Рассмотрим ее действие на простом примере:
var a = { foo: 1, bar: 2 }; var b = Object. extend(a, { bar: 3 }); // b == { foo: 1, bar: 3 }
Object. extend() заменяет атрибуты первого объекта атрибутами второго. Те атрибуты, которые во втором объекте не определены, остаются неизменными. Функция возвращает получившийся объект.
Используем эту функцию для имитации наследования:
// Базовый класс var Greeter = Class. create(); Greeter. prototype = { initialize: function(name) { this. name = name; },
greet: function() { alert(‘Hello, ‘ + this. name); } };
// Дочерний класс var MusicGreeter = Class. create(); MusicGreeter. prototype = Object. extend(Greeter. prototype, { // Конструктор остается от базового класса
// Перегружаем метод приветствия greet: function() { alert(‘Playing ‘ + this. getMusic() + ‘ for ‘ + this. name); },
// Добавляем новый метод getMusic: function() { return ‘Moon Sonata’; } });
var alexGreeter = new MusicGreeter(‘Alex’); alexGreeter. greet(); // Playing Moon Sonata for Alex Проблема, которая часто возникает при переопределении метода в дочернем классе – это необходимость вызова метода базового класса. Это можно реализовать, скопировав необходимый метод в прототипе базового объекта в «запасной» атрибут, значение которого не будет затерто функцией Object. extend():
// Базовый класс var Greeter = Class. create(); Greeter. prototype = { initialize: function(name) { this. name = name; },
greet: function() { alert(‘Hello, ‘ + this. name); } };
// Копируем функцию Greeter. prototype. _baseGreet = Greeter. prototype. greet;
// Дочерний класс var MusicGreeter = Class. create(); MusicGreeter. prototype = Object. extend(Greeter. prototype, { // Конструктор остается от базового класса
// Перегружаем метод приветствия greet: function() { // Вызываем метод базового класса this. _baseGreet(); } }); Статические функции и переменные
Статические атрибуты доступны без создания экземпляров класса, поэтому они определяются вне прототипа и конструктора:
var Greeter = Class. create(); Greeter. prototype = { initialize: function(name) { this. name = name; },
greet: function() { alert(‘Hello, ‘ + this. name); // Обращаемся к статической переменной Greeter. greetCount++; } };
Greeter. greeterCount = 0; // Статическая переменная // Статическая функция Greeter. displayGreetCount = function() { alert(‘Total greetz made: ‘ + Greeter. greetCount); } Контроль доступа
Во всех объектно-ориентированных языках существуют средства для скрытия членов класса от доступа «извне». Придется огорчить читателя – в JavaScript таких средств не существует.
Однако, данные все же можно скрыть, организовав доступ к ним через аксессоры:
var Greeter = Class. create(); Greeter. prototype = { initialize: function(n) { var name = n;
this. setName = function(new_name) { name = new_name; };
this. getName = function() { return name; } },
greet: function() { alert(‘Hello, ‘ + this. getName()); } }; В этом примере переменная name не инициализируется в публичном интерфейсе this, но является локальной переменной конструктора. Так как аксессоры (setName и getName) так же определяются в конструкторе, они имеют к ней доступ.
У такой техники существует один сильный недостаток, о котором говорилось в начале статьи – это перерасход памяти. Аксессоры setName и getName будут созданы для каждого экземпляра, так как инициализируются в конструкторе. Другая методика, которую обычно использую я – это именование всех private-атрибутов с символа подчеркивания. Такой подход, конечно, не защищает их от изменения извне, но все же вероятность изменения их по ошибке значительно снижается.
ЗаключениеКак вы видите, JavaScript – гибкий язык, дающий программисту простор как для реализации желаемых идей, так и для совершения ошибок. При программировании на нем будьте аккуратны и помните о дисциплине - и ошибок стане меньше, а гибкость и динамичность языка проявят себя во всей красе. Вернутся Вам будет интересно:
|
- Публикации
Итак, у вас имеется свой собственный форум, но его никто не посещает? Мы готовы помочь вам справиться с этой нелегкой проблемой.
Данная статья в основном рассчитана на тех, кто самостоятельно занимается продвижением своего проекта.
Проклял все настройки кодировок, проклял DirectAdmin и еле-еле нашел то место где располагается и задается общий пароль для ...
Твиттер уже давно стал из нового web 2. 0 сервиса рекламной площадкой с огромным количеством потенциальных пользователей ...
Самое время кратко описать, чем нам придется заниматься. Прежде всего нужно понять, что блог ты будешь делать для получения ...
Долго не знал с чего начать данную статью. Статья что-то типа мыслей вслух и философии.