Итак, классов в JavaScript нет, но к версии 2.0, они вроде как должны добавиться.
Но зато в JavaScript'e есть подобие классов -- объекты. Так вот, любой литерал
(число, строка) представляет собой экземпляр "класса" Number и String соответственно
(тут правда есть некоторые особенности, о которых, в рамках этой статьи я писать не буду, есть материал, но сайт похоже не работает, как только наладится -- я выложу);
массив -- Array; объект -- Object, функция -- Function.
Основная часть
Так вот, самый универсальный конструктор создается при помощи конструктора Function.
(Обратите внимание я сказал именно конструктор! Тоесть записи:
CODE:
function xxx() {}
var xxx = function() {}
var xxx = new Function('');
var xxx = function() {}
var xxx = new Function('');
создают именно конструкторы, но не объекты! А в случае с Array, Object:
var xxx = new Object(); мы уже создаем экземпляр и уже не имеем возможности создавать свои экземпляры созданного объекта.)
В JavaScript есть и наследование (prototype-based), если в объекте нет определенного свойства/метода оно ищется в его прототипе, если нет в прототипе, то в прототипе-прототипа, и.т.д. Оно работает по схеме отличной от привичного ООП, но есть функции для исправления этого недостатка. (http://dklab.ru/chicken/nablas/38.html;
http://dklab.ru/chicken/nablas/39.html;
http://dklab.ru/chicken/nablas/40.html -- для более опытных.)
В реализации функции для эмуляции привычного ООП (из 40 наблы) автор вообще отказался от прототипного наследования! Хотя там и задается прототип, наследование не происходит из-за того, что в первый вызов конструктор тутже завершается. Дальше вызывается этот же конструктор, но уже в прототипе создаваемого конструктора, таким образом происходит добавления свойств/методов.
Так, как это статья ориентирована на начинающих, я просто покажу сампл объекта и сделаю комментарии, поскольку полностью описывать все особенности наследования,
и.т.п. я не собираюсь, выше я дал ссылки на статьи, в которых все очень хорошо разьесняно.
CODE:
function myClass()
{
//var self = this; "дедовский метод"
this.property = 1; //Свойство
this.method = function() //метод
{
}
var property = 1; //Private свойство
var method = function() // Private метод
{
//self.property = 3; "дедовский метод"
}
}
var xxx = new myClass(); //экземпляр
{
//var self = this; "дедовский метод"
this.property = 1; //Свойство
this.method = function() //метод
{
}
var property = 1; //Private свойство
var method = function() // Private метод
{
//self.property = 3; "дедовский метод"
}
}
var xxx = new myClass(); //экземпляр
Такая схема создания объекта, годится для библиотек, где требуется создание экземпляров объекта. (Замечание: если вам нужно использовать public свойство/метод
в private методе, нужно его вызывать в контексте конструктора, поскольку все переменные, в JavaScript (даже локальные) вызываются в контексте конструктора window. Пример method.apply(this, [массив аргументов]), method.call(this, арг1, арг2, e.t.c.)
или "дедовским методом", создаете локальную переменную ссылку на this и используете внутри прайвит метода её. Пример есть в коде выше -- закомментированые строки.)
Для библиотек, где нужно просто пространство имен:
CODE:
var library = {
'property': 1,
'method': function()
{
}
}
'property': 1,
'method': function()
{
}
}
Этот случай, когда вам не требуются private методы/свойства, а когда они требуются,
пишите так:
CODE:
var library = new function()
{
//все как в первом случае (многоэкземплярном)
}
{
//все как в первом случае (многоэкземплярном)
}
Справедливости ради замечу, что создать тут экземпляр все таки можно, но это станет
делать только идиот.
Делается так:
CODE:
var library2 = new library.constructor();
Вот впринципе все! Есть предложения -- пишите. Дополню.
Это v.0.1b статьи, прошу если что исправить пунктуацию, грамотность (отправлять в ЛС).
(Добавление)
Вот мой вариант эмулляции истинного наследования, без использования прототипов.
Несмотря на одинаковое название работать нужно несколько иначе, второй аргумент должен являтся конструктором, тоесть экземпляром объекта Function. Почему я выбрал такой подход? Да потому, что в данном случае у меня каждый раз при создании экземпляра, получившегося благодаря функции newClass конструктора, будет создаваться полностью новый объект, а в случае кода из той наблы, будет использоваться один, общий экземпляр.
CODE:
<pre><script>
/**
* @author Aleksandr Michalicyn <admin@systemnik.net.ru>
* @version 1.0
*/
function newClass(parent, new_constructor)
{
var class = function()
{
class.new_constructor.apply(this, arguments);
if (class.parent_constructor)
{
this.parent = {};
class.parent_constructor.apply(this.parent, arguments);
for (var k in this.parent)
{
if (!this.hasOwnProperty(k))
{
this[k] = this.parent[k];
}
}
}
}
class.prototype = {};
class.new_constructor = new_constructor;
if (parent)
{
class.parent_constructor = parent;
}
return class;
}
// Базовый "класс".
Car = newClass(null, function() {
document.writeln("Вызван конструктор Car().");
this.drive = function() {
document.writeln("Вызван Car.drive()");
}
});
// Производный "класс".
Zaporojets = newClass(Car, function() {
document.writeln("Вызван конструктор Zaporojets().");
this.drive = function() {
document.writeln("Вызван Zaporojets.drive()");
return this.parent.drive.call(this);
}
this.crack = function() {
document.writeln("Вызван Zaporojets.crack()");
}
});
document.writeln("Программа запущена.");
// Создаем объект производного "класса".
var vehicle = new Zaporojets();
vehicle.drive(); // вызывается функция базового объекта
// Создаем еще один объект того же класса.
var vehicle = new Zaporojets();
vehicle.crack(); // функция производного объекта
</script></pre>
/**
* @author Aleksandr Michalicyn <admin@systemnik.net.ru>
* @version 1.0
*/
function newClass(parent, new_constructor)
{
var class = function()
{
class.new_constructor.apply(this, arguments);
if (class.parent_constructor)
{
this.parent = {};
class.parent_constructor.apply(this.parent, arguments);
for (var k in this.parent)
{
if (!this.hasOwnProperty(k))
{
this[k] = this.parent[k];
}
}
}
}
class.prototype = {};
class.new_constructor = new_constructor;
if (parent)
{
class.parent_constructor = parent;
}
return class;
}
// Базовый "класс".
Car = newClass(null, function() {
document.writeln("Вызван конструктор Car().");
this.drive = function() {
document.writeln("Вызван Car.drive()");
}
});
// Производный "класс".
Zaporojets = newClass(Car, function() {
document.writeln("Вызван конструктор Zaporojets().");
this.drive = function() {
document.writeln("Вызван Zaporojets.drive()");
return this.parent.drive.call(this);
}
this.crack = function() {
document.writeln("Вызван Zaporojets.crack()");
}
});
document.writeln("Программа запущена.");
// Создаем объект производного "класса".
var vehicle = new Zaporojets();
vehicle.drive(); // вызывается функция базового объекта
// Создаем еще один объект того же класса.
var vehicle = new Zaporojets();
vehicle.crack(); // функция производного объекта
</script></pre>