Проблема в том, что в javascript нет привычного наследования. Максимум что можно сделать, это создать конструктор класса:
function Class(){/*тут инициализируем поля *}А потом, через прототип, добавить методы, константы и статические переменные, которые будут одни на все экземпляры.
Class.prototype = {/*Методы*/}Этого в большинстве случаев хватает, но иногда хочется большего.
А если очень хочется...
… то можно это реализовать. Классический способ выглядит так:
function inherit_A(Child, Parent)
{
var F = function () { };
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.super = Parent.prototype;
}И его даже можно использовать:
$(document).ready(function ()
{
function Man(name) { this.name = name }
Man.prototype.say = function () { console.log("My name is " + this.name) }
function Gentleman(name) { Man.call(this, name); }
inherit_A(Gentleman, Man);
});
У меня с ним, правда, есть одна маленькая проблема — обычно, для создания прототипа, я использую объектную нотацию, как было показано немного выше:
Class.prototype =
{
/*Методы*/
}
А, с этим наследованием, таким методом не очень попользуешься — или мои функции полностью затрут унаследованный прототип, или унаследованный прототип полностью затрет мои функции. К счастью, наследовать можно и по-другому.
Метод посвящается всем любящим объектные литералы
Вот и он:
function inherit_B(Child, Parent)
{
var F = function () { };
F.prototype = Parent.prototype;
var f = new F();
for (var prop in Child.prototype) f[prop] = Child.prototype[prop];
Child.prototype = f;
Child.prototype.super = Parent.prototype;
}Он просто берет, и копирует все свойства из прототипа родителя в прототип наследника, так что теперь мой любимый объектный литерал работает как надо:
$(document).ready(function ()
{
function Man(name) { this.name = name }
Man.prototype =
{
cosntructor: Man,
THOUGHTS: "wanna beer!",
say: function ()
{
console.log("My name is " + this.name + " and i think:'" + this.THOUGHTS + "'")
}
}
function Gentleman(name, prefered_beverage)
{
Man.call(this, name);
this.prefered_beverage = prefered_beverage;
}
Gentleman.prototype = { cosntructor: Gentleman, THOUGHTS: "it's teatime!" }
inherit_B(Gentleman, Man)
function Programmer(name, prefered_lang)
{
Gentleman.call(this, name, "Cofee");
this.prefered_lang = prefered_lang;
}
Programmer.prototype =
{
cosntructor: Programmer,
THOUGHTS: "runtime error 138? wanna debug XD!"
}
inherit_B(Programmer, Gentleman)
var man = new Man("Jack");
var gentleman = new Gentleman("John", "Orange pekoe");
var programmer = new Programmer("James", "C++");
man.say();
gentleman.say();
programmer.say();
});И консоль со мной согласна:
sample.js:11 My name is Jack and i think:'wanna beer!' sample.js:11 My name is John and i think:'it's teatime!' sample.js:11 My name is James and i think:'runtime error 138? wanna debug!'
Отлично. А теперь представим гипотетическую ситуацию, когда родительских классов много, и нужно, например, вызвать какой-нибудь метод, который был перегружен 2-3 класса назад. В такой ситуации, конечно, лучше еще раз хорошенько присмотреться к архитектуре своего приложения. Теперь допустим, что там все работает, как задумано, тогда чертовски неудобно писать, например, так:
this.super.super.super.someMethod.apply(this)
Но это тоже решаемо
function inherit_C(Child, Parent)
{
var F = function () { };
F.prototype = Parent.prototype;
var f = new F();
for (var prop in Child.prototype) f[prop] = Child.prototype[prop];
Child.prototype = f;
Child.prototype[Parent.prototype.__class_name] = Parent.prototype;
}Эта функция в прототип потомка добавляет объект с ссылкой на прототип родителя. Чтобы она заработала как надо, в прототип каждого класса нужно добавить поле
__class_name
Вот, например, предположим, что в нашей иерархии появился класс BadProgrammer, которому как раз понадобился удобный доступ к прототипу далекого предка. Немного модифицируем предыдущий пример:
$(document).ready(function ()
{
function Man(name) { this.name = name }
Man.pro
THOUGHTS: "wanna beer!",
say: function ()
{
console.log("My name is " + this.name + " and i think:'" + this.THOUGHTS + "'")
}
}
function Gentleman(name, prefered_beverage)
{
Man.call(this, name);
this.prefered_beverage = prefered_beverage;
}
Gentleman.prototype =
{
__class_name: "Gentleman",
cosntructor: Gentleman,
THOUGHTS: "it's teatime!"
}
inherit_C(Gentleman, Man)
function Programmer(name, prefered_lang)
{
Gentleman.call(this, name, "Cofee");
this.prefered_lang = prefered_lang;
}
Programmer.prototype =
{
__class_name: "Programmer",
cosntructor: Programmer,
THOUGHTS: "runtime error 138? wanna debug XD!"
}
inherit_C(Programmer, Gentleman)
function BadProgrammer(name)
{
Programmer.call(this, name, "brainfuck");
}
BadProgrammer.prototype =
{
__class_name: "BadProgrammer",
cosntructor: BadProgrammer,
THOUGHTS: "runtime error 138? wanna debug XD!",
say: function () { this.THOUGHTS = this.Man.THOUGHTS; this.Man.say.apply(this); }
}
inherit_C(BadProgrammer, Programmer)
var man = new Man("Jack");
var gentleman = new Gentleman("John", "Orange pekoe");
var programmer = new Programmer("James", "C++");
var badprogrammer = new BadProgrammer("Jake");
man.say();
gentleman.say();
programmer.say();
badprogrammer.say();
});totype =
{
__class_name: "Man",
cosntructor: Man,
THOUGHTS: "wanna beer!",
say: function ()
{
console.log("My name is " + this.name + " and i think:'" + this.THOUGHTS + "'")
}
}
function Gentleman(name, prefered_beverage)
{
Man.call(this, name);
this.prefered_beverage = prefered_beverage;
}
Gentleman.prototype =
{
__class_name: "Gentleman",
cosntructor: Gentleman,
THOUGHTS: "it's teatime!"
}
inherit_C(Gentleman, Man)
function Programmer(name, prefered_lang)
{
Gentleman.call(this, name, "Cofee");
this.prefered_lang = prefered_lang;
}
Programmer.prototype =
{
__class_name: "Programmer",
cosntructor: Programmer,
THOUGHTS: "runtime error 138? wanna debug XD!"
}
inherit_C(Programmer, Gentleman)
function BadProgrammer(name)
{
Programmer.call(this, name, "brainfuck");
}
BadProgrammer.prototype =
{
__class_name: "BadProgrammer",
cosntructor: BadProgrammer,
THOUGHTS: "runtime error 138? wanna debug XD!",
say: function () { this.THOUGHTS = this.Man.THOUGHTS; this.Man.say.apply(this); }
}
inherit_C(BadProgrammer, Programmer)
var man = new Man("Jack");
var gentleman = new Gentleman("John", "Orange pekoe");
var programmer = new Programmer("James", "C++");
var badprogrammer = new BadProgrammer("Jake");
man.say();
gentleman.say();
programmer.say();
badprogrammer.say();
});Класс BadProgrammer воспользовался мыслями самого первого звена нашей эволюционной цепочки классов, и, теперь, думает совсем не о том, о чём обычно думают программисты 
My name is Jack and i think:'wanna beer!' My name is John and i think:'it's teatime!' My name is James and i think:'runtime error 138? wanna debug!' My name is Jake and i think:'wanna beer!'
И еще кое что
Не представляю в какиx случаях это может пригодится, но, возможно, когда-нибудь, кому-нибудь может понадобится множественное наследование. Его тоже вполне можно реализовать:
function inhertit_multiple(child)
{
for( var i = 1; i < arguments.length; ++i )
{
var parent = arguments[i]
for (var prop in parent.prototype)
{
if (!child.prototype[prop]) child.prototype[prop] = parent.prototype[prop];
}
child.prototype[parent.prototype.__class_name] = parent.prototype;
}
}Не очень-то и сильно отличается от предыдущей версии.
Для того, чтобы показать как оно работает, я придумал еще один вполне реалистичный пример:
$(document).ready(function ()
{
function Mammy() { this.mammy_message = "You Dont love me!" }
Mammy.prototype =
{
__class_name: "Mammy",
say_something_wise: function () { console.log(this.mammy_message) }
}
function Daddy() { this.daddy_message = "I just don't want to be a dad!" }
Daddy.prototype =
{
__class_name: "Daddy",
say_something_wise: function () { console.log(this.daddy_message) }
}
function Lad()
{
this.lad_message = "And i want a candy!";
Mammy.apply(this);
Daddy.apply(this);
}
Lad.prototype =
{
__class_name: "Lad",
say_something_wise: function ()
{
this.Daddy.say_something_wise.call(this);
this.Mammy.say_something_wise.call(this);
console.log(this.lad_message);
}
}
inhertit_multiple(Lad, Mammy, Daddy)
var lad = new Lad();
lad.say_something_wise();
});Если это запустить, то в консоли появится примерно то что мы и ожидаем
I just don't want to be a dad! You Dont love me! And i want candy!


