JavaScript — очень динамический язык, в нём заложена возможность менять язык под себя и создавать удобные инструменты для дальнейшей работы. «Реализация классического наследования» — как раз один из таких инструментов. В данный момент я не представляю себе, как я программировал бы на JS без «классов».
Для меня «Классы» — это, скорее, подход к проектированию и реализации поставленной задачи. В нашем новом проекте такой подход используется по полной программе (и, я думаю, что он оправдывает себя на все 100%).

На данный момент существует море реализаций «Классов в JS».
Я бы выделил два их них: это реализации в Prototype.js и в Base2. В обоих способах нельзя вызвать «другой» метод родительского класса (т.е. нельзя вызвать parent::A из функции this.B) — я считаю это существенным недостатком: бывает так, что нельзя выполнить задачу не изменив сам подход.
В своей реализации «Классического наследования в Javascript» я использовал немного «иной» синтаксис:
Я условно разделил методы класса на два вида:
Должно выскочить 4 алерта по порядку:
Для меня «Классы» — это, скорее, подход к проектированию и реализации поставленной задачи. В нашем новом проекте такой подход используется по полной программе (и, я думаю, что он оправдывает себя на все 100%).

Эта статья написана в качестве приложения к другой моей статье «Компоненты в Unobtrusive JavaScript» (хотя, в принципе, может выступать в роли самостоятельного текста).
На данный момент существует море реализаций «Классов в JS».
Я бы выделил два их них: это реализации в Prototype.js и в Base2. В обоих способах нельзя вызвать «другой» метод родительского класса (т.е. нельзя вызвать parent::A из функции this.B) — я считаю это существенным недостатком: бывает так, что нельзя выполнить задачу не изменив сам подход.
В своей реализации «Классического наследования в Javascript» я использовал немного «иной» синтаксис:
// вызов метода myMethod родительского класса
this.myMethod.__parent(param1);
* This source code was highlighted with Source Code Highlighter.
Для реализации такой конструкции, возможно, пришлось чуть-чуть пожертвовать производительностью: не все методы Класса находятся в прототипе функции-инициализатора. Но я знаю, что плюсов от использования подхода гораздо больше.Я условно разделил методы класса на два вида:
- Обычные методы. Они хранятся в прототипе функции-конструкторе
- «Виртуальные» методы. Такие методы, например, __constructor и __destructor, требуют возможности вызова методов родительского класса. Хранятся они не в прототипе. А в thisObj попадают в функции-инициализаторе объекта.
JooS.Class = JooS.Reflect(null, {
__common: new Object(),
__destroy: function() {
arguments.length ? this.__destructor.apply(this, arguments) : this.__destructor();
},
__constructor: function() {
alert("JooS.Class::__constructor");
},
__destructor: function() {
alert("JooS.Class::__destructor");
}
});
var Class1 = JooS.Reflect(JooS.Class, {
myFunction: function() {
this.__constructor.__parent();
},
__constructor: function() {
alert("Class1::__constructor");
this.myFunction();
},
__destructor: function() {
alert("Class1::__destructor");
this.__destructor.__parent();
}
});
var Obj1 = new Class1(); // Создание объекта
Obj1.__destroy(); // Подготовка к удалению объекта
delete Obj1; // Удаление объекта
* This source code was highlighted with Source Code Highlighter.
Должно выскочить 4 алерта по порядку:
- «Class1::__constructor»
- «JooS.Class::__constructor»
- «Class1::__destructor»
- «JooS.Class::__destructor»
var JooS = {
Reflect: (function(F) {
return function(source, methods) {
if (!methods)
methods = { };
if (source) {
if (!source.prototype.__constructor)
source.prototype.__constructor = source;
F.prototype = source.prototype;
}
else
F.prototype = { constructor: source = F };
var c;
if (methods.__constructor) {
F.prototype = new F;
c = this.Virtual(F, { __constructor: methods.__constructor }).prototype.__constructor;
c.prototype = new F;
delete methods.__constructor;
}
else {
c = function() {
if (this.__constructor)
this.__constructor.apply(this, arguments);
};
c.prototype = new F;
}
c.constructor = source;
return c.prototype.constructor = this.Virtual(c, methods);
}
})(new Function),
Virtual: (function(hidden) {
var __index = 1;
var __extend = function(constructor, name, __self) {
var __parent = constructor.prototype[name], method;
if (typeof __self == "function") {
var __selfName = __self.__name = name + "::" + __index++;
constructor.prototype[__selfName] = __self;
if (__parent && typeof __parent == "function") {
var __thisObj, __originalParent = __self.__parent = __parent.__self || __parent;
method = function(a1, a2, a3) {
__thisObj = this;
__parent = __originalParent;
switch (arguments.length) { // I'm cheater. Almost...
case 0: return this[__selfName]();
case 1: return this[__selfName](a1);
case 2: return this[__selfName](a1, a2);
case 3: return this[__selfName](a1, a2, a3);
default: return __self.apply(this, arguments);
}
};
method.__parent = function(a1, a2, a3) {
var o = __parent, r;
__parent = o.__parent;
switch (arguments.length) {
case 0: r = __thisObj[o.__name](); break;
case 1: r = __thisObj[o.__name](a1); break;
case 2: r = __thisObj[o.__name](a1, a2); break;
case 3: r = __thisObj[o.__name](a1, a2, a3); break;
default: r = o.apply(__thisObj, arguments);
}
__parent = o;
return r;
};
method.__self = __self;
}
}
constructor.prototype[name] = method || __self;
};
for (var IE = "IE" in { toString: IE })
hidden = false;
return function(constructor, methods) {
for (var name in methods)
__extend(constructor, name, methods[name]);
if (hidden) // thanks to Andrea Giammarchi
for (var i=0; i<hidden.length, name = hidden[i]; i++)
if(methods.hasOwnProperty(name))
__extend(constructor, name, methods[name]);
return constructor;
};
})([ "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", "toLocaleString", "toString", "valueOf" ]),
Mixin: function(destination, source) {
for (var i in source.prototype)
if (!destination.prototype[i] && i != "constructor")
destination.prototype[i] = source.prototype[i];
},
Clone: (function(F) {
return function(Obj) {
F.prototype = Obj || { };
return new F();
};
})(new Function),
Extend: function(destination, source) {
for (var i in source)
destination[i] = source[i];
return destination;
}
};
* This source code was highlighted with Source Code Highlighter.