Объекты

## Методы

### Объявление

Словарь может хранить ссылку на функцию, называемую в таком случае методом, которая объявляется следующим образом:

```
function <словарь>.<имя>(<аргументы>) dict
  <тело>
endfunction

```

В остальном объявление метода аналогично объявлению обычной функции.

### Вызов

Для вызова метода используется команда **call**:

```
call <словарь>.<имя>(<параметры>)

```

Команда **call** может использоваться в качестве функции:

```
call call(<имя>, <массивПараметров>, <словарь>)

```

* **МассивПараметров** - массив значений, передаваемых в вызываемый метод в качестве параметров (если метод не принимает аргументов, необходимо передать пустой массив)
* **Словарь** - необязательный параметр, определяющий словарь, в контексте которого будет вызван метод

В остальном вызов метода аналогичен вызову обычной функции.

### Переменная self

В методе может использоваться переменная **self**, ссылающаяся на словарь, в контексте которого вызывается метод.

## Прототипная реализация класса

Функция **extend**, выполняющая копирование элементов одного словаря в другой, может использоваться для прототипной реализации класса. Для этого достаточно определить метод-конструктор следующим образом:

```
let <класс> = {}
function <класс>.new(<аргументы>) dict
  let l:obj = {<значенияПоУмолчанию>}
  let l:obj = extend(l:obj, <класс>)
  return l:obj
endfunction

```

Дополнительные методы могут быть объявлены в контексте этого класса.

### Наследование

Для реализации наследования, достаточно объединить все классы иерархии с помощью вызовов функции **extend**:

```
let <родитель> = {}
...

let <потомок> = {}
function <потомок>.new(<аргументы>) dict
  let l:obj = {<значенияПоУмолчанию>}
  let l:obj = extend(l:obj, <родитель>.new(<параметрыРодителя>))
  let l:obj = extend(l:obj, <потомок>)
  return l:obj
endfunction

```

### Переопределение методов

Для переопределения методов родительского класса в дочернем, может применяться функция **call**:

```
function <потомок>.<имя>(<аргументы>) dict
  return call(<родитель>.<имя>, [], self)
endfunction

```

## Примеры использования

Использование переменной **self**:

```
let s:obj = {'x': 1}
function s:obj.getX() dict
  return self.x
endfunction

echo s:obj.getX()

```

Объявление и использование класса:

```
let s:Parent = {}
function! s:Parent.new(a) dict
  return extend({'a': a:a}, s:Parent)
endfunction

function! s:Parent.setA(a) dict
  let l:self.a = a:a
endfunction

function! s:Parent.getA() dict
  return l:self.a
endfunction

let s:pobj = s:Parent.new('foo')
echo s:pobj.getA() " foo

```

Пример наследования классов:

```
let s:Child = {}
function! s:Child.new(a, b) dict
  return extend(extend({'b': a:b}, s:Parent.new(a:a)), s:Child)
endfunction

function! s:Child.setB(b) dict
  let l:self.b = a:b
endfunction

function! s:Child.getB() dict
  return l:self.b
endfunction

" Дальнейшее наследование
let s:SubChild = {}
function! s:SubChild.new(a, b, c) dict
  return extend(extend({'c': a:c}, s:Child.new(a:a, a:b)), s:SubChild)
endfunction

```

Пример переопределения метода:

```
let s:Child = {}
function! s:Child.new(a, b) dict
  return extend(extend({'b': a:b}, s:Parent.new(a:a)), s:Child)
endfunction

function! s:Child.getA() dict
  return call(s:Parent.getA, [], l:self) . l:self.b
endfunction

```

Пример использования примесей:

```
let s:Publisher = {}
function! s:Publisher.new() dict
  return extend({'listeners': {}}, s:Publisher)
endfunction

let s:Class = {}
function! s:Class.new() dict
  return extend(extend({}, s:Publisher.new()), s:Class)
endfunction

```

Проверка вхождения класса в иерархию с помощью "Утиной типизации":

```
function! s:instanceof(obj, class)
  for l:assertFun in keys(filter(a:class, 'type(v:val) == 2'))
    if !has_key(a:obj, l:assertFun)
      return 0
    endif
  endfor

  return 1
endfunction

echo s:instanceof(s:childObject, s:Parent) " 1
echo s:instanceof(s:childObject, s:SubChild) " 0

```

## Смотрите также

* Словари
* Функции