CoffeeScript принёс в JS неплохую абстракцию классов, основанную на прототипах.
Реализовав известную модель наследования и дополнив её наследованием методов касса,
он позволяет легко строить иерархии классов, даже не зная о цепочках прототипов.
Но и эта модель может быть улучшена.
Приведенный способ не сможет полностью заменить существующий,
так как он использует свойство
Но он позволяет значительно расширить возможности наследования, работая при этом
поверх основной модели.
Кроме создания цепочки прототипов конструктров кофе использует следующий код
для наследования свойств класса:
То есть все свойства просто копируются. При таком наследовании теряется гибкость.
Простейший пример — при изменении метода предка не меняются методы в
наследованных классах. Также не наследуются неперечисляемые свойства.
Было бы гораздо лучше, если свойства класса тоже наследовались по цепочке
прототипов. Всё что нужно — после наследования класса средствами кофе удалить
всё унаследованное :) и установить
При таком наследовании у дочернего класса будут доступны все свойства предка,
которые так же можно переопределить. Но появляется возможность реализовывать
интересную функциональность, основанную на том, что свойства принадлежат
прототипу, а не самому объекту.
Один из примеров — переменная экземпляра класса (class instance variable).
UPD: Все таки уточню, что переменная относится к классу, не к его экземпляру.
переменная_экземпляра класса. Доступна в качестве свойства только одного класса.
Этот подход к наследованию лежит в основе пакета coffee_classkit.
В этом пакете также реализованы методы работы с классами, взятые из Ruby:
хуки
они идиентичны аналогам из руби, только названия в кэмлкейсе.
Кто не не знаком с Ruby, надеюсь, без труда всё поймёт по исходнику,
тем более, что методы не больше шести строк.
Вся функциональность доступна с использованием обычного синтаксиса объявления класса:
Для удобвства в пакете есть класс, имеющий все эти методы в своем составе.
Унаследовав от него класс, можно использовать их в более явной и привычной форме:
Также в пакет включен аналог
Больше простых примеров можно найти в тестах в репозитории.
С использованием описанных подходов, становится возможным писать модульный
объектно-ориентированный код, не врываясь в глобальное пространство имён.
Развёрнутый пример можно посмотреть в набросках проекта,
написанного с использованием CoffeeClasskit.
Реализовав известную модель наследования и дополнив её наследованием методов касса,
он позволяет легко строить иерархии классов, даже не зная о цепочках прототипов.
Но и эта модель может быть улучшена.
Приведенный способ не сможет полностью заменить существующий,
так как он использует свойство
__proto__
, недоступное в некоторых реализациях JS.Но он позволяет значительно расширить возможности наследования, работая при этом
поверх основной модели.
Кроме создания цепочки прототипов конструктров кофе использует следующий код
для наследования свойств класса:
for key of parent
child[key] = parent[key] if __hasProp_.call(parent, key)
То есть все свойства просто копируются. При таком наследовании теряется гибкость.
Простейший пример — при изменении метода предка не меняются методы в
наследованных классах. Также не наследуются неперечисляемые свойства.
Было бы гораздо лучше, если свойства класса тоже наследовались по цепочке
прототипов. Всё что нужно — после наследования класса средствами кофе удалить
всё унаследованное :) и установить
child.__proto__ = parent
.При таком наследовании у дочернего класса будут доступны все свойства предка,
которые так же можно переопределить. Но появляется возможность реализовывать
интересную функциональность, основанную на том, что свойства принадлежат
прототипу, а не самому объекту.
Один из примеров — переменная экземпляра класса (class instance variable).
UPD: Все таки уточню, что переменная относится к классу, не к его экземпляру.
переменная_экземпляра класса. Доступна в качестве свойства только одного класса.
Object.defineProperty Parent, 'test',
get: -> @_test if @hasOwnProperty '_test'
set: (val) -> @_test = val
Parent.test = 1
Parent.test # => 1
Child.test # => undefined
Child.test = 2
Parent.test # => 1
Child.test # => 2
Этот подход к наследованию лежит в основе пакета coffee_classkit.
В этом пакете также реализованы методы работы с классами, взятые из Ruby:
include
, использующий append_features
, extend
, использующий extend_object
,хуки
inherited, included, extended
. Не стану здесь описывать их подробно:они идиентичны аналогам из руби, только названия в кэмлкейсе.
Кто не не знаком с Ruby, надеюсь, без труда всё поймёт по исходнику,
тем более, что методы не больше шести строк.
Вся функциональность доступна с использованием обычного синтаксиса объявления класса:
classkit = require 'coffee_classkit'
class Child extends Parent
classkit.extendsWithProto @
classkit.include @, Mixin
Для удобвства в пакете есть класс, имеющий все эти методы в своем составе.
Унаследовав от него класс, можно использовать их в более явной и привычной форме:
class Example extends classkit.Module
@extendsWithProto()
@include Mixin
Также в пакет включен аналог
ActiveSupport::Concern
:class Mixin extends classkit.Module
@extendsWithProto().concern()
@includedBlock: ->
# выполняется в контексте базового класса
@instanceVariable 'test'
class @ClassMethods
someClassMethod: ->
someInstanceMethod: ->
class Base extends classkit.Module
@include Mixin
@someClassMethod()
(new Base).someInstanceMethod()
Больше простых примеров можно найти в тестах в репозитории.
С использованием описанных подходов, становится возможным писать модульный
объектно-ориентированный код, не врываясь в глобальное пространство имён.
Развёрнутый пример можно посмотреть в набросках проекта,
написанного с использованием CoffeeClasskit.