Блог Конфуция
Ручное наследование в яваскрипте 06.04.2011

Наследование без фреймворков вызывает приступ паники даже у матерых яваскриптеров. Особенно, если надо не просто добавлять новые методы, а модифицировать поведение родительских. А между тем, процедура очень проста.

Просто класс #

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(){ … })().

Тесты тут.

Теги:
  • клиент
  • javascript
  • frameworkless
Очень жду ваших комментариев на почту или на гитхаб.