Наследование без фреймворков вызывает приступ паники даже у матерых яваскриптеров. Особенно, если надо не просто добавлять новые методы, а модифицировать поведение родительских. А между тем, процедура очень проста.
function A () {}
var a = new A()
Да, как не удивительно, класс — это просто функция.
var o = new function () {}
Классом может быть любая функция. Даже безымянное замыкание право имеет.
function A () {}
A.prototype =
{
method: function () {}
}
var a = new A()
a.method()
Методы добавляются с помощью прототипов. Про прототипы мы всё уже знаем.
function A () {}
A.staticMethod = function () {}
A.staticMethod()
В экземплярах staticMethod доступен не будет.
function A () {}
function B () {}
B.prototype = new A()
Да, именно prototype = new A(), а не просто prototype = A().
function A () {}
A.prototype =
{
methodA: function () {}
}
function B () {}
B.prototype = new A()
var b = new B()
b.methodA()
Получили в экземпляре класса B полезный methodA() от класса A.
function A () {}
A.prototype =
{
methodA: function () {}
}
function B () {}
B.prototype = new A()
B.prototype.methodB = function () {}
var b = new B()
b.methodA()
b.methodB()
Теперь в экземпляре класса B есть оба полезных метода.
function A () {}
function B () {}
B.prototype = new A()
var methodsB =
{
methodB1: function () {},
methodB2: function () {},
methodB3: function () {}
}
Object.extend(B.prototype, methodsB)
var b = new B()
b.methodB1()
b.methodB2()
b.methodB3()
Object.extend(dst, src) просто копирует свойства из src в dst. Если договориться с санитарами, можно заменить Object.extend() на цикл for in ;)
function A () {}
A.prototype =
{
methodA: function () {}
}
function B () {}
B.prototype = new A()
B.prototype.methodA = function () { alert(42) }
var b = new B()
b.methodA()
Вылетит алерт.
function A ()
{
this.array = []
}
function B ()
{
A.apply(this)
}
B.prototype = new A()
var b1 = new B()
var b2 = new B()
console.log(b1.array == b2.array)
//>>> false
Если бы не выполнили A.apply(this), то array был бы один на все экземпляры B.
function A () {}
A.prototype =
{
methodA: function () {}
}
function B () {}
B.prototype = new A()
B.prototype.superMethodA = B.prototype.methodA
B.prototype.methodA = function ()
{
this.superMethodA()
}
var b = new B()
b.methodA()
Прекрасно работает в простых случаях.
function A () {}
A.prototype =
{
methodA: function () {}
}
function B () {}
B.prototype = new A()
B.prototype.methodA = function ()
{
A.prototype.methodA.apply(this)
}
function C () {}
C.prototype = new B()
C.prototype.methodA = function ()
{
B.prototype.methodA.apply(this)
}
var c = new C()
c.methodA()
Прекрасно работает во всех случаях.
function A () {}
A.prototype =
{
methodA: function () {}
}
function B () {}
B.prototype = new A()
var superMethodA = A.prototype.methodA
B.prototype.methodA = function ()
{
superMethodA.apply(this)
}
var b = new B()
b.methodA()
Работает пошустрее, но сломается, если кто-то изменит A.prototype.
function A () {}
A.prototype =
{
setPosition: function (x)
{
this.x = x
}
}
function B () {}
B.prototype = new A()
B.prototype.setPosition = function (x, y)
{
A.prototype.setPosition.call(this, x)
this.y = y
}
function C () {}
C.prototype = new B()
C.prototype.setPosition = function (x, y, z)
{
B.prototype.setPosition.call(this, x, y)
this.z = z
}
var c = new C()
c.setPosition(1, 2, 3)
В результате скрытого от нас последовательного вызова A#setPosition() и B#setPosition() внутри C#setPosition(), мы получим объект с данными {x: 1, y: 2, z: 3}.
Все кусочки кода, конечно же, обернуты в (function(){ … })().
Тесты тут.