Как стать автором
Обновить

Комментарии 94

Хм… стоит ли выделять из реализаций неудачные? Mootools?
Если честно, я не смотрел в сторону MooTools =(
А у вас есть ссылка, чтобы почитать про это?
Я сам всё нашёл. Вот тут: mootools.net/downloads/mootools-1.2-core-nc.js в коментах написано:
Inspiration:
— Class implementation inspired by [Base.js](http://dean.edwards.name/weblog/2006/03/base/) Copyright © 2006 Dean Edwards, [GNU Lesser General Public License](http://opensource.org/licenses/lgpl-license.php)
Так что MooTools можно и не выделять — оно основано на реализованном в base2
Убит :)
Однако не могу не заметить, что синтаксис МТ видится мне более изящным — слово inspired применено не зря.
НЛО прилетело и опубликовало эту надпись здесь
А я и использую «прототипное наследование» (в основном).
А использовать «чистое прототипное наследование» мешает отсутствие читаемого и удобного метода повторного использования кода.
НЛО прилетело и опубликовало эту надпись здесь
Удобно — это «на вкус, на цвет товарища нет» =)
Лично мне удобно иметь функцию «расширения прототипа», удобно иметь функцию, настраивающую «мои виртуальные методы», удобно называть некоторые функции — «Классами», удобно писать вызов функции родительского класса не указывая имя этого класса и не писать при этом конструкции вида parentClassName.prototype.myMethod.call(this, param1)

> var ExcavationRobot = new GenericRobot();
В этой конструкции не хватает как минимум кода инициализации объекта.
НЛО прилетело и опубликовало эту надпись здесь
У меня понимание языка и реализация классов шли нога в ногу =)
Я как-то нашёл одну замечательную фразу:
Язык, как средство выражения мыслей, и мышление, как процесс производства мыслей, теснейшим образом взаимосвязаны и взаимообусловлены. Взаимозависимость языка и мышления проявляется, упрощенно говоря, в том, что мысли отливаются в формы, удобные для их выражения средствами языка, а язык устроен таким образом, чтобы наиболее адекватно отражать сформировавшиеся мысли.
Короче говоря, я хочу называть эту надстройку «реализацией классов», потому что мне удобно «думать классами» =)
НЛО прилетело и опубликовало эту надпись здесь
Мне тоже кажется, что стремление прикрутить к яваскрипт классы — лукавое. Язык уже содержит механизмы наследования. Нужно только научиться ими пользоваться.
Вы против удобной формы записи прототипного наследования? Удобство — от лукавого?
Просто не вижу смысла усложнять код слоем абстрагирующим prototype based inheritance в class based. Используйте структуры и средства языка.
А удобно то, что хорошо представляешь как работает. Для некоторых классова парадигма наследования может показаться менее удобной. Все зависит от опыта и знаний.
Я думаю современные библитотеки предоставляют средства для классого ооп в javascript не потому что в этом есть какието преимущества. Просто потому что многим не понятна схема наследования через прототипы.
Этот смысл многие не видят =( На самом деле тут всё зависит от задачи. У меня задача была — появился механизм, а потом — понеслось…
А в остальном — согласен.
У Котерова есть реализация классического наследования, в том числе вызов родительских методов.
dklab.ru/chicken/nablas/40.html
Оно перестаёт работать на третьем шаге наследования (вроде бы)
Да, он на форуме от статьи отрёкся.
В Mootool точно есть наследование классов, сам им не пользуюсь.
Заметил когда прикручивал одну штуку к сайту.
В MooTools есть 2 режима наследования Extends (с возможно доступа к переопределенным методам), а также множественное наследование Implements.

Примеры можно посмотреть в документации по классам MooTools
я штото похожее делал только синтаксис немного был другой
для вызова родительского конструктора this.__base([param1, param2,...])
для вызова родительской функции this.__base('Method',[param1, param2,...]);

Если кому интересно то покажу реализацию
Знаете, вот смотрю я на это и понимаю, что для того, что бы это понять и использовать надо быть гуру JavaScript, но к сожалению при работе изучить до такой степени его очень тяжело за отсутствием времени у большинства программистов. Т.е. либо нужно быть голым JS программером, либо должна отсутствовать личная жизнь/учеба/работа.
Гуру JS не используют искусственное натягивание чужеродной парадигмы :)
Используют, используют — Psih уже назвал меня гуру (только… ник у него подозрительный =)
Psih всего лишь гуру mysql :) А гуру JS камментом ниже радуется, что ES4 обломался )
гуру сам за себя отвечает, а вот Вам — (Вы тоже гуру?) — лично Вам, чем понравился облом ES4? :) Только, пожалуйста, обоснованно, а не минусами =) И Вы ничего не ответили на мои ответы по поводу Ваших высказываний о «гуру и --чужеродных-- парадигмах»
Умерьте пыл, пожалуйста :)
В каком месте я радовался про ES4? ES4 это было что-то далёкое и аморфное и не для браузеров по большому счёту, даже. Меня он вообще не касается.
По поводу Ваших ответов — слишком много букаф в ответ на полушутливую фразу.

Чего мне не нравится?
Два экрана кода, сопоставимого по объёму с кодом многих сайтов, только для написания обёртки.
Не нравится каждый раз при разборе сайта рассматривать как на этот раз очередной автор наворочал обёрток и что это за непонятные конструкции, которыми усеян весь остальной код.

Впрочем, если автор написал себе удобную обёртку и понимает что он пишет и для чего, то я рад за него вне зависимости от того, называет он её _class или нет.
Но когда это начинает в огромных количествах вылезать в сеть в качестве обучающих примеров я начинаю нервничать.
> Умерьте пыл, пожалуйста :)

да ну, бросьте, я еще и не начал заводится (и не горю желанием, и думаю, надобности нет) =)

Мне просто показалось, что Вы несколько… эмм… =) — поддерживаете чьи-то слова, при этом процент собственных мыслей по этому вопросу у Вас меньше (сказал, настолько корректно, насколько мог ;)) (ну а иначе — к чему подобные утверждения («Гуру JS не используют искусственное натягивание чужеродной парадигмы», «гуру JS… радуется, что ES4 обломался»), написанные от третьего лица, а не от Вашего?)

> По поводу Ваших ответов — слишком много букаф

ах… пардон, вопрос исчерпан.
Ну я же не могу писать про гуру от своего лица :)
Да что вы все привязались — «чужеродная парадигма, чужеродная парадигма! классы не нужны!» =) и что с того, что на данный момент в JS только делегирующая прототипная модель наследования? — ну нравится человеку называть свою обертку «классами». Ведь по сути, использует-то он для реализации этой обертки все ту же делегирующую к прототипам конструкторов модель. И не важно, как он это называет — Class, _class, moyaUdobnayaObyortkaDlyaPrototypnogoNasledovaniya или еще как — *суть не меняетя*! Главное, — что человек понимает, *что* он пишет *и для чего*.

[.quote: «#Zork 25 сентября 2008, 16:43 „]
> А чем мешает прототипное наследование? Оно же, как бы язык то другой, парадигма как бы тоже другая, зачем с++ опять городить?

Так все и используют *единственное* prototype-based inheretance (другого нет), просто называют свои обертки, как им удобно.

Другое дело (и вот это, действительно, очень жаль), когда человек может нести всякий бред, говоря: я выучил JS по Prototype.js (или jQuery/Mootools/Ext — не важно) и при этом еще может начать яро доказывать, что в JS есть классы (т.к. Class.create() =))

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

P.S. ES4 (JS2), к сожалению (или к счастью =)) обломался, так что классов пока не будет :( Но ведь их планировали. Хотя они были бы похожи на классы из Руби и Питона (динамическая часть классов, т.к. планировались еще и неизменяемые, как в C++). Да, вот хороший пример — Питон — там есть классы, но там так же можно динамически менять слоты класса (аналогия — смена слотов в протоипе конструктора в JS) и слоты порожденного инстанса (как и в JS).
НЛО прилетело и опубликовало эту надпись здесь
> ES4 (JS2), к сожалению (или к счастью =)) обломался
к счастью ;)
> к счастью ;)

пожалуй ) Вы как в воду глядели, когда у себя в блоге на винграде в теме «ECMA-разборок» надеялись, что что-нибудь не срастется в ES4 =)
А не факт =)
Может быть JS стал бы в языком программистов, не для разработчиков интерфейсов и дизайнеров…
Ну я не думаю, что наличии статических Java-классов — признак того, что язык стал для «программистов», а не для «разработчиков интерфейсов и дизайнеров» =) Мне видится это заблуждением.

Возьмем, к примеру, Python (а конкретно, — его построение классов; — это для тех, кто любит придраться к словам и начнет мне сейчас говорить — «нашел, что сравнивать — Питон и JS!» — повторю — только лишь *абстрактно* — работу классов в Питоне). Так вот — абстрагируемся от технической реализации (хотя, там так же налицо — делегация — если свойство не найдено в родном объекте, его поиск продолжается в цепи классов (цепи прототипов конструкторов в JS)):

Py:

# создали класс A, присвоили инстанс-свойству «а» значение 10 (__init__ — играет роль «конструктора»)

class A(object):
def __init__(self):
self.a = 10

JS:

// аналог на JS (вместо понятия «класс» — функция-конструктор)

function A() {
this.a = 10;
}

Py:

# класс B, наследуемый от А, self-свойство «b» = 20

class B(A): # наследование
def __init__(self):
self.b = 20
A.__init__(self)

JS:

// аналог на JS

function B() {
this.b = 20;
B.superClass.prototype.constructor.call(this, arguments);
}

// наследование (имеем возможность достучаться через цепь прототипов до нужного свойства)

var __inheritance = function() {};
__inheritance.prototype = A.prototype;
B.prototype = new __inheritance();
B.prototype.constructor = B;
B.superClass = A; // явная ссылка на «А» для вызова родительских методов

Далее, объекты-инстансы:

obj = B() # Py:
var obj = new B() // JS:

print obj.a, obj.b # 10, 20 — self-свойства
alert([obj.a, obj.b]) // 10, 20 — this-свойства

Расширяем класс в Питоне и прототип конструктора в JS новым слотом:

A.c = 30 # Py:
A.prototype.c = 30 // JS:

Свойство посредством делегирования (во всяком случае в JS) доступно через конструкцию «роднойОбъект.свойство»

print obj.c # Py: 30
alert(obj.c) // JS: 30

Добавляем новые родные свойства:

obj.d = 40 # Py: 40
obj.d = 50 // JS: 40

Добавляем свойство с таким же именем в класс (Питон) и прототип конструктора (JS) — на сей раз — «B»:

B.d = 70
B.prototype.d = 70

Пока берется родное «d»:

print obj.d # Py: 40
alert(obj.d) // JS: 30

Удаляем:

del obj.d #Py:
delete obj.d // JS:

А вот теперь — снова за счет делегирования из класса (прототипа конструктора):

print obj.d # Py: 70
alert(obj.d) // JS: 70

Так вот, Covex, скажите мне — разве Питон не язык «программистов»? В нем, как Вы видите, нет Java-классов.

А для тех, кто при выходе очередной статьи про «*оболочки* в виде классов в JS» не ленится каждый раз написать — «зачем классы?! это вам не С++!», скажите мне — где в примерах выше вы увидели С++? Тем не менее, приводились тоже классы. И, в виду того, что мы абстрагировались, визуальная разница — это наследование:

Py: class B(A):

JS:

var __inheritance = function() {};
__inheritance.prototype = A.prototype;
B.prototype = new __inheritance();
B.prototype.constructor = B;
B.superClass = A; // явная ссылка на «А» для вызова родительских методов

И вот, чтобы не писать каждый раз эту длинную конструкцию, люди делают свои обертки и называют их «классами», и, во всяком случае я, в этот момент не думают ни о каких классах из С++. Для меня это просто обертка, я знаю, что что бы я ни написал — внутри будет — *prototype-based inheritance*.
> его поиск продолжается в цепи классов (цепи прототипов конструкторов в JS)

Всё-таки «цепь прототипов конструкторов» — это другая цепь, к моменту поиска «класс», он же конструктор, может тихо издохнуть, и это никак не скажется на цепи прототипов объекта, где и производится поиск, это к вопросу, как тяжело точно описывать в родных терминах, слишком уж много слова «prototype» везде и всюду. Основная роль конструктора — связать новый объект с прототипом и его (объект) вернуть, а то, что внутри конструктора можно обвешивать новый объект слотами — весЧь второстепенная, слабо характеризующая конструктор с точки зрения класса. Увы, в js нет лаконичных терминов (раз) и есть микс чужеродных терминов (два), а язык пиплов стремится к простоте, вот и лезут классы/хеши там, где их. Победить это возможно строгим терминологически лаконичным разбором происходящего, что сделать очень непросто…

> B.superClass.prototype.constructor.call(this, arguments);

Масло масляное, есть же уже ссылка на нужный конструктор.

> alert(obj.d) // JS: 30

50
> Основная роль конструктора — связать новый объект с прототипом и его (объект) вернуть

да, Zeroglif, это я знаю, именно поэтому цепь прототипов — implicit, а не явная — объект имеет доступ к прототипу через нужный слот (в одной из реализации — __proto__; кстати, все в том же Python это свойство называется __class__ — и обращаясь из объекта-инстанса можно так же, как и в JS расширить класс (прототип) — «obj.__class__.new_method = lambda self, x: x + 1» — новый метод, который будет доступен всем уже существующим инстансам и последующим), и конструктор смотрит на прототип через свой слот (prototype) — понятно, что конструктор может издохнуть.

Я всего лишь проводил абстрактную аналогию, чтобы показать, что классы — это не только С++.

> Победить это возможно строгим терминологически лаконичным разбором происходящего, что сделать очень непросто…

да, и это тоже верно — только вот там концов не сыщешь — откуда какая терминлогия пошла (так, примеру, (и я не знаю, кто писал эту статью) в Википедии в разделе Python написано (справа сверху) — «оказал влияние на:… Ruby, Boo, Groovy, *ECMAScript*» — я не знаю, насколько это может быть правдой, хотя — почему нет?)

> 50

а, да, пардон — но это просто опечатки (я не тестировал; копи-паст)
>> > B.superClass.prototype.constructor.call(this, arguments);
> Масло масляное, есть же уже ссылка на нужный конструктор.

Ы )) естественно =) это я просто, чтобы показать, как вызвать другие методы родительского класса, конструктор же можно было вызвать — B.superClass.apply(this, arguments);
о! =) и еще одно проверил в Python (я его знаю пока на среднем уровне, поэтому новое что-то узнавать всегда приятно):

> он же конструктор, может тихо издохнуть, и это никак не скажется на цепи прототипов объекта, где и производится поиск

class A: pass

a = A()

# видим, что и класс "А" и инстанс "а" присутствуют в глобальном объекте
print globals() # вернет объект, где свойства - все глобальные переменные

# расширим класс через инстанс

a.__class__.b = 10

print a.b # 10

# а теперь класс "тихо издыхает"
del globals()['A']

# проверим
print globals() # "A" - исчез

# однако, ествественно, не исчез сам объект класса
print a.__class__ # <class __main__.A at 0x00E243F0>

# т.е. исчезла *лишь ссылка на объект-класс*
# аналогия с JS - конструктор издох (который в 
# этом плане служил ссылкой на прототип),
# но объект-инстанс имеет связь с классом

a.__class__.c = 20
a.c # 20

А вот создать новый инстанс класса "А" уже не получится (ссылки нет):
    
b = A() # .error

Я исходил от другого. У Python и JavaScript немного разное положение:
Python — серверный язык. Как я понял — он возник после PHP, Perl и всего такого — не нужен он дизайнерам и оформителям интерфейсов. Изучать его будут люди, знающие другие языки программирования (начинающие — не в счёт =)

JavaScript — язык браузера… Перед написанием придумал аргумент: «Вот, глянте ка, на компани.яндекс.ру в вакансиях только разработчик интерфейсов есть» (а там ещё аж 2 вакансии — JS и JS-гуру =). Пойду с другой стороны.

Кроме него ничего нет. Даже его нет — есть jQuery и т.п. Всеобщий накопленный багаж знаний о языке позволяет либо делать какие-нибудь поделки, например через onmouseover и onmouseout менять картинку; либо делать простые вещи используя простой фреймвёрк. Это может сделать любой. Непрограммист, желая войти на рынок труда в качестве «разработчика интерфейсов», легко в него войдёт. Программист же не захочет, потому что если не копать глубже — это «не язык, а лажа какая-то» (я так не думаю, — Прим.ред.).

Вобщем, естественный отбор, эволюция и всё такое =)
> Python — серверный язык
> JavaScript — язык браузера

Все это, возможно, — *пока*; JavaScript и на данный момент — достаточно мощный язык; и тот факт, что основное его применение пока реализуется в песочнице в виде браузера, не означает, что он не найдет более серьезного применения в дальнейшем, нежели onmouseover/onmouseout =) — к тому же, для меня JS (как и для многих, уверен, в этом блоге) — это не только визуальные эффекты. И более того, тот же XUL использует JS, в Java встроен JS-интерпретатор Rhino.

Кстати, в статье о JS в Википедии есть забавная цитата:

JavaScript в данный момент полностью занимает нишу браузерных языков. Несмотря на то, что по слухам некоторые разработчики браузеров встраивают (или уже встроили) в дополнение к JavaScript-у такой язык как Python, для динамического изменения веб-страниц на стороне клиента, официальной информации по этому вопросу нет.


=) тоже не знаю, на сколько это правда (так — слухи), но тут обратная ситуация, которую Вы описали — Питон может стать «языком браузеров».

> Как я понял — он возник после PHP, Perl и всего такого

ну Перл-то понятно — появился раньше (1987), а вот PHP и JS (1995) — позже Python'a (1990).

> Даже его нет — есть jQuery

А про фреймворки — это вообще отдельный разговор, я уже упоминал не раз, что фреймворк может помочь не только с преодолением рутины, но и в (возможно) равной степени с отупением.

Перефразирую классика:
> > ES4 (JS2) питон, к сожалению (или к счастью =)) обломался
> к счастью ;)

Это я к тому, что он не станет стандартом-«языком браузеров», как JS (из-за IE в первую очередь). Хотя, от Microsoft можно чего угодно ожидать: в IE8 — стремление к стандартам, в IE9 — питон, в IE10 — ES4 =))

А вообще, нужно к питону присмотреться повнимательнее, хотя, без практического применения будет сложновато =(
> А вообще, нужно к питону присмотреться повнимательнее, хотя, без практического применения будет сложновато =(

да, одной из причин, в виду которых мне понравился Питон, была схожесть с JS (в отличии от того же PHP, например): делегирующая модель наследования (хотя в Питоне это названо «классом», а не «прототипом» — см. примеры выше — если абстрагироваться от технической реализации — практически полное сходство с JS (или наоборот — JS с Питоном)); замыкания (хотя в PHP 5.3 они тоже появились); mutable-объекты; и т. д.

Поэтому, я думаю, сложностей не будет.

> Это я к тому, что он не станет стандартом-«языком браузеров», как JS (из-за IE в первую очередь)

да ну не о стандартах я говорю =) я сам только сегодня узнал, что Питон уже давно Gecko'вцами поддерживается (хотя написано, что в Мозилле с версии 1.9 вообще появился). Ссылки были лишь на свой же комментарий из Википедии о «Python + JS» (но я не думаю, что «Python vs. JS» ;)). А вот что будет — посмотрим =) В конце концов, в Мозилла фаундэшн тоже-таки не идиоты сидят.
Поэтому, я думаю, сложностей не будет.
Сложности — только организационные. Если будет задача (например, «админка на питоне в виде расширения к FF =)»- всё будет интересно и просто; не будет задачи — будет всё наоборот
да ну не о стандартах я говорю
я имел ввиду «стандарт-дефакто», а не w3c какой-нибудь )
А вот что будет — посмотрим =)
+1: Посмотрим =) Это же самое, кстати, и про майкрософт можно сказать:
Сейчас они против, а через год-два, если с коммерческой точки зрения будет выгодно (пусть даже косвенно), поменяют своё решение.
> от Microsoft можно чего угодно ожидать:… в IE10 — ES4

кстати, а по поводу ES4 (JS 2), представители от Microsoft были в числе первых, кто отрицал этот проект в пользу JS (максимум) 1.6-1.7.
Я исходил от другого. У Python и JavaScript немного разное положение:
Python — серверный язык. Как я понял — он возник после PHP, Perl и всего такого — не нужен он дизайнерам и оформителям интерфейсов. Изучать его будут люди, знающие другие языки программирования (начинающие — не в счёт =)

JavaScript — язык браузера… Перед написанием придумал аргумент: «Вот, глянте ка, на компани.яндекс.ру в вакансиях только разработчик интерфейсов есть» (а там ещё аж 2 вакансии — JS и JS-гуру =). Пойду с другой стороны.

Кроме него ничего нет. Даже его нет — есть jQuery и т.п. Всеобщий накопленный багаж знаний о языке позволяет либо делать какие-нибудь поделки, например через onmouseover и onmouseout менять картинку; либо делать простые вещи используя простой фреймвёрк. Это может сделать любой. Непрограммист, желая войти на рынок труда в качестве «разработчика интерфейсов», легко в него войдёт. Программист же не захочет, потому что если не копать глубже — это «не язык, а лажа какая-то» (я так не думаю, — Прим.ред.).

Вобщем, естественный отбор, эволюция и всё такое =)
Не понял. Почему в прототайпе нельзя? А как же параметр $super, передаваемый неявно в каждый метод?
Потому что $super ссылается на одноимённую функцию родительского класса. В другой функции текущего класса такая ссылка недоступна.
а, понятно, окей
Вообще похоже на корявый костыль =) Не ужели сложно написать что-то более удобочитаемое.
Например base.Method(). В прочем я не js разработчик…
внесу свои 5 копеек:
var Class = function(){};

Class.create = function(prop, cls_super){
    
    if (!cls_super){
        cls_super = Class;
    }
    
    var init = false;
    // new class
    var cls = function (){
        if (init) {
            // super
            this.super = function (name) {
                return this.constructor.super(this, name);
            }
            
            // extend this
            this.extend = function (prop) {
                for (name in prop) {
                    this[name] = prop[name];
                }
                return this;
            }

            if (this.init) {
                this.init.apply(this, arguments);
            }
        }
    };

    cls.prototype = new cls_super();
    init = true;

    // extend class
    for (name in prop) {    
        cls.prototype[name] = prop[name];
    }

    cls.prototype.constructor = cls;

    // super
    cls.super = function(instance, name){
        var property = cls_super.prototype[name];
        if (property instanceof Function){
            return function () {property.apply(instance, arguments)};
        } else {
            return property;
        }
    }

    // extend
    cls.extend = function(prop){
        init = false;
        var _cls = Class.create(prop, cls);
        init = true;
        return _cls;
    }
    
    return cls;
}


//
// end
//




вот пример использования:
var Animal = Class.create({init: function(kind){this.kind = kind;}})
var Dog = Animal.extend({
    init: function(name, kind) {
        this.super('init')(kind);
        this.name = name;
    }
});

var doggy = new Dog('Rex', 'wolf');
В вашем коде есть баг. Вот как эго воспроизвести:
var C1 = Class.create({ init: function() { alert("C1"); } });
var C2 = C1.extend({
 init: function() {
  alert("C2");
//  debugger;
  this.super('init')();
 }
});

var C3 = C2.extend({ });
var C4 = C3.extend({
 init: function() {
  alert("C4");
  this.super('init')();
 }
});

var obj = new C4();

* This source code was highlighted with Source Code Highlighter.
Ожидается, что выскочит 3 алерта: С4, С2, С1. А на самом деле работа скрипта закончится ошибкой «too much recursion» =(

Разница между нашими подходами в том, что вы — наследуете друг от друга классы, а я — функции.
да, действительно баг:
            // super
            this.super = function (name) {
                return cls.super(this, name);
            }


идёт рекурсивный вызов, придётся убрать this.super и заменить на class.super

профиксил:
var Class = function(){};

Class.create = function(prop, cls_super){
    
    if (!cls_super){
        cls_super = Class;
    }
    
    var init = false;
    // new class
    var cls = function (){
        if (init) {
            // extend this
            this.extend = function (prop) {
                for (name in prop) {
                    this[name] = prop[name];
                }
                return this;
            }

            if (this.init) {
                this.init.apply(this, arguments);
            }
        }
    };

    cls.prototype = new cls_super();
    init = true;

    // extend class
    for (name in prop) {    
        cls.prototype[name] = prop[name];
    }

    cls.prototype.constructor = cls;

    // super
    cls.super = function(instance, name){
        var property = cls_super.prototype[name];
        if (property instanceof Function){
            return function () {return property.apply(instance, arguments)};
        } else {
            return property;
        }
    }

    // extend
    cls.extend = function(prop){
        init = false;
        var _cls = Class.create(prop, cls);
        init = true;
        return _cls;
    }
    
    return cls;
}



ваш код тогда будет выглядеть так:
var C1 = Class.create({ init: function() { alert("C1"); } });
var C2 = C1.extend({
 init: function() {
  alert("C2");
//  debugger;
  C2.super(this, 'init')();
 }
});

var C3 = C2.extend({ });
var C4 = C3.extend({
 init: function() {
  alert("C4");
  C4.super(this, 'init')();
 }
});

var obj = new C4();



вот думаю как сделать:
1) class.super(объект, имя_метода)
или
2) class.super(имя_метода, объект)

сейчас используется 1 вариант
все как мантру повторяют «наследование, инкапсуляция, полиморфизм» и даже не задумываются о том, что наследование и инкапсулящия противоречат друг другу. наследование — распространённая, но не слишком удачная схема реюзинга кода, порождающая жёсткую зависимость внутренней структуры потомков от внутренней структуры родителя.
> что наследование и инкапсулящия противоречат друг другу

ну, не обязательно, для это и сделали оговорки в виде «public / protected / private»; и где-то (опять — тот же Питон) это соглашение осуществляется на уровне именования переменных.

> наследование — распространённая, но не слишком удачная схема реюзинга кода, порождающая жёсткую зависимость внутренней структуры потомков от внутренней структуры родителя

ну, да — альтернатива — примеси и агрегация; а можно даже и совмещать — что мешает унаследовать общую структуру, агрегировать еще двух «родителей», плюс к этому подмешать еще три объекта? =)
оговорки и соглашения не решают проблему взаимной зависимости.
примеси — довольно глупая концепция разделения методов и данных (привет процедурному программированию). вот штрихи и агрегация — да, альтернатива.
а используя всего по чуть-чуть — получишь кашу-малашу.
> наследование и инкапсулящия противоречат друг другу
заметьте — я ни слова не сказал про инкапсуляцию =)

> порождающая жёсткую зависимость
если честно, я не вижу здесь ничего страшного (может быть мне не хватает теории). я всегда считал, что это не проблема, а задача: самое главное придумать правильную иерархию классов — и тогда всё будет хорошо.
то есть ты отрицаешь инкапсуляцию, как основополагающий принцип ооп? :-)

самое главное — написать простой, понятный, эффективный и легко поддерживаемый код. создание иерархии классов предназначено для создания понятного кода. но вот рефакторить код с «иерархией классов» довольно сложно: менять внутренности одного родителя можно только с постоянной оглядкой на всех детей.
Ничего я не отрицаю, просто в JS классов то нету. Всё ж открыто; конструкции вида
(function() {
// код
})();
… удобны, но не всегда. Так что, имхо, все попытки «открыть только интерфейс работы с данными» превратятся в разработку костылей (я осознаю, что JS-код статьи многие тоже считают костылём =))
простой, понятный, эффективный и легко поддерживаемый код.
Это такой код, к виду которого привыкло большинство или разработчик… Да и нет для JS критериев «понятности», «простоты» и «удобства»! Такой уж язык.
рефакторить код с «иерархией классов» довольно сложно
Рефакторить сложно только «мегобайты кода». Мне кажется это не про JS (точнее — это не про околосайтовые задачи).
> просто в JS классов то нету. Всё ж открыто;

наличие сущности «класс» не означает присутствие «строгой» инкапсуляции (создающей концепцию «черного ящика»)

Часто инкапсуляция может быть достигнута простейшими организационными мерами: Знание того, что «вот так-то делать нельзя» иногда является самым эффективным средством инкапсуляции!


Как пример — опять же Питон — уровни доступа свойств там определены на уровне именования. Так, private-свойство (обозначается двумя ведущими подчеркиваниями) __приватноеСвойство будет недоступно извне. Однако, при определении класса, оно преобразуется к виду _ИмяКласса__приватноеСвойство и доступно как public — «Все ж открыто;».
есть там классы, просто на них не висит таблички с надписью «class» =)
класс (в общем случае) — объект представляющий из себя группу объектов имеющих некоторый общий признак.
конкретно в программировании — объект предназначенный для порождения однотипных объектов.
конкретно в яваскрипте — функция, предназначенная для создания объектов.
думаешь в яваскрипте просто так существует оператор instanceof? ;-)

я не против открытости. более того — при отладке открытость просто необходима. и инкапсуляция не запрещает открытость. инкапсуляция — это независимость внутренних полей и методов объекта от других объектов. именно это свойство позволяет разрабатывать объекты независимо друг от друга.

сложность рефакторинга слабо зависит от объёмов кода, а больше зависит от его сложности. и в десяти килобайтах можно такого наворотить, что разобраться будет нереально, а сделать одно не сломав другого — вообще невозможно.
> конкретно в яваскрипте — функция, предназначенная для создания объектов.

неа, если уж миксовать терминологии (т. е. абстрактно отойти от спецификации ECMAScript), то роль класса в JS будет играть прототип (и именно с ним работает instanceof), а не конструктор. Вот как раз здесь, выше параллель организации Питона и JS — конструктор (ссылка на класс) умирает, но инстанс посредством своей ссылки (__class__) имеет доступ к цепи классов (в JS — умирает конструктор (функция), но объект имеет доступ к протоипному объекту посредством __proto__).
это бага в instanceof ;-)
после new мы пишем имя функции
после instanceof — тоже имя функции
а прототип — всего-лишь прототип, подборка слотов, которые будут отнаследованы только если функция это позволит (не определит их у объекта при создании)
> это бага в instanceof ;-)

не, ну я ж уточнил — "абстрактно отойти от спецификации", а не кардинально, иначе отсебятина получится

> после new мы пишем имя функции

все правильно, и задача этой функции — проинициализировать объект и создать неявную (это важно!) связь с прототипом. Все. На этом ее работа закончена и она может исчезнуть, освободив драгоценную память.

> после instanceof — тоже имя функции

тоже все правильно, это оператор анализирует цепь прототипов для определения значения:

function Q (){};
Q.prototype.q = 10;
var q = new Q();

alert(q.q); // 10 - посредством делегирования

alert(q instanceof Q); // true

// обnullяем прототип
Q.prototype = null;

// за счет неявной цепи все имеем связь с прототипным объектом
alert(q.q);

// но вот instanceof уже не может достучаться до
// прототипного объекта через ссылку Q.prototype
alert(q instanceof Q); // .error


* This source code was highlighted with Source Code Highlighter.


> не, ну я ж уточнил — «абстрактно отойти от спецификации», а не кардинально

Мне кажется, здесь основной вопрос в подходе:
— можно знать как всё работает на самом деле, и разрабатывать, исходя из этого знания;
— можно объяснять реальное положение дел исходя из этой теории, и разрабатывать исходя из теории.
Это две крайности — все в какой-то степени используют оба подхода. И получается, что вроде бы делаем одно и тоже, а получается по разному =)
… исходя из этой какой-то теории…
Да, ествественно, однако, всегда убедительней звучат высказывания, основанные на первом подходе (во всяком случае — легче приводить аргументы, ссылаясь на «первоисточник», который должен являться «интерфейсом дискуссии/спора» — т. е. тем, что принимается априори, то, с чем согласны обе стороны; далее, эти рамки можно расширить (абстрагируюсь), но изначально — «первоисточник» — это удобный старт для начала разговора).

С другой стороны, второй подход, когда пытливо сам разрабатываешь «какую-то теорию», пытаясь самостоятельно (возможно, основываясь на каких-то эмпирических опытах) объяснить то или иное поведение системы, может служить хорошей тренировкой мозгов. Но! ;) Как правило, в итоге этих исследований (если все расчеты были верны и все опыты положительны), все равно приходишь к спецификации. =) (это как в старой песне: «кто-то заметил, а кто-то спросил, а третий — запишет ответ»).
И, конечно же, любая новая теория — есть синтез уже ранее существовавших. Тут еще один важный момент — «первоисточник» тоже люди писали и (чисто гипотетически) они могли ошибиться (или же по прошествии некоторого времени осознать неэффективность той технологии, которую описали). Вот тогда, «первоисточник» переписывается и вводятся новые технологии (и терминологии).

Но это так — лишь философски и топика не касается ;)
у-упс =)

> старт для начала
> эмпирических опытах

масло масляное в масле с маслом =))))
ок, убедил :)
Мне больше нравится думать, что «new» и «instanceof» — это терминологическая натяжка из той же серии, откуда в названии языка слово «Java». Делали prototype-based язык, местами вынужденно опираясь на class-based, вышло, что вышло… ;)
Да, все та же «проблема» миксовки терминологий, но, повторю, часто концов не сыщешь, кто был первым в терминологии, и не поймешь, кто на кого ссылался при создании своих сущностей.

Поэтому, главное, я считаю, — понимание общих закономерностей (на формулах, на буквах) и уже потом привязка к конкретным терминологиям (на числах). И здесь иногда удобно абстрагироваться от «конкретных чисел» (но, ясно-понятно, только лишь в терминологии для проведения параллелей, иначе, опять повторюсь, получится отсебятина). Ну а самое главное, если речь все-таки идет о конкретной технологии, чтобы этих абстрактных отхождений от терминологии спецификации было немного, иначе — снова — терминологическая каша. ;)
> штрихи

а что за штрихи?
примеси без состояния и зависящие только от публичных методов предназначенные для расширения интерфейса.

пример: www.ruby-doc.org/core/classes/Comparable.html
достаточно определить оператор и подцепить штрих Comparable, чтобы можно было использовать и любые другие операторы сравнения.
подожди, что-то я не нашел четкого определения «штрих» (дай ссылку, пожалуйста)

> примеси — довольно глупая концепция разделения методов и данных
> штрихи — примеси без состояния

вообще, в определении примеси как раз говорится, о том, что состояние (данные) определены в классе, а поведение (методы) подмешиваются. В первой цитате ты называешь это глупой концепцией, а во второй определяешь «штрих» определением «примесь». Или я не правильно понял?
Я погуглил, нашёл вот это lj.rossia.org/users/ringill/10156.html и наконец-то понял что такое «штрихи» и «примеси»!
Штрихи — это, действительно, примеси. Там, видимо, при компиляции должны накладываться ограничения на доступ к переменным. Т. к. эти решения должны быть реализованы на уровне языка, то как и с «инкапсуляцией в JS» это всё можно реализовать только «простейшими организационными мерами» ©

Как оказалось, у меня реализован этот механизм, но я назвал это «плагином»: JooS.Plugin =)
Суть в следующем: прототип класса расширяется каким-то объектом (у меня — набором функций другого абстрактного класса). Этот набор должен включать какую-то функцию «инициализации» этого штриха/примеси. Эта функция должна быть вызвана в конструкторе класса

http://beatle.joos.nnov.ru/ — теперь тут «анимированные PNG» можно таскать по странице мышкой.
( это ссылка на «абстрактный класс», а это ссылка на расширенный через штрих/примесь класс )
там немного неправильно написано. примесь не может содержать данных, ибо тогда она была бы обычным суперклассом при множественном наследовании.

плохо, что ты засовываешь всю эту функциональность в один объект. аргрегация тут была бы более уместна. например, при драгстарте нужно запоминать исходные координаты объекта и исходные координаты мыши, чтобы при при перемещении мыши правильно позиционировать объект. где хранить эту информацию? в куче, вместе с остальной инфой объекта? а почему он должен знать о внутренних переменных какого-то там «плагина»?
Я плагинами и хотел реализовать «множественное наследование» (только чтобы не было косяков с именованиями) — получилось «как получилось» =)

Что касается данного «плагина», то в данный момент самом плагине содержатся только «дефолтные реализации методов» © — значит это как минимум «примесь»; плагин не содержит своих данных — значит это «штрих» =)
tenshi, посмотри пожалуйста, правильно ли я тебя понял.

Я сегодня во второй раз (надеюсь в последний) «наконец-то понял про штрихи и примеси».
Переименую функцию в JooS.Mixin (потому что в общем случае плагин будет «примесью»), но буду стараться, чтобы каждый отдельный плагин получался «штрихом».
> но буду стараться, чтобы каждый отдельный плагин получался «штрихом».

так а че там стараться? — главное, как и при описании абстрактного класса, указать какие именно методы должен реализовать класс, использующий штрих; а дальше уже штрих будет работать с этими методами.
Тут весь вопрос в обосновании «исключений из правила»: если нельзя, но очень хочется, то можно.

Например, «штрих» может не быть «пустым интерфейсом».
В нём могут содержаться какие-то конечные функции/методы. Для них могут понадобится какие-то свои данные, которые в конечном итоге будут содержатся в thisObj.
Эту штуку нельзя будет назвать «штрихом» — это фактически будет «примесь» (если я всё правильно понял =)

С другой стороны — сейчас есть только понимание, а конкретных задач пока нет. Посмотрим как всё получится =)
> В нём могут содержаться какие-то конечные функции/методы. Для них могут понадобится какие-то свои данные, которые в конечном итоге будут содержатся в thisObj.

да, создание состояния (this-свойств) в штрихе может породить конфликт состояний (программист, использующий Ваш штрих может завести в классе свое свойство «а», в то время как Вы использовали его в штрихе; в итоге работа штриха будет нарушена).

Кстати, некоторые (например, PHP) эту проблему решают специфичным именованием — так PHP оставляет за собой право на использование любых имен, с двумя ведущими подчеркиваниями (__construct, __destruct, __set, __get, и т. д.), что потенциально, если не знать об этом соглашении, может создать конфликт имен. Так и здесь — в документации к абстрактному классу (штриху) можно указать, что штрих оставляет за собой право на именование this-свойство, начинающийхся с фразы BlaBla (BlaBla_check, BlaBla_set, и т. д.). Но, это, все же, компромиссное решение, а не на уровне автоматизации.
> нашёл вот это

а-а )) у меня, оказывается, эта ссылка была в закладках еще чуть ли не год назад. Но я положил ее в закладки и забыл — да и не дочитал до места со «штрихами». Спасибо =)
www.iam.unibe.ch/~scg/Research/Traits/

штрихи — это подмножество примесей, а примеси — подмножество «множественного наследования», а оно есть подмножество «реюзинга кода». при этом концепции их таковы:
реюзинг — не надо писать одно и то же дважды
наследование — берём существующий класс и хачим его до нужного нам поведения
примеси — создаём класс, примешиваем другой класс с дефолтными реализациями методов и хачим получившегося монстра
штрихи — создаём класс с минимально необходимым интерфейсом, примешиваем другой класс реализующий другой интерфейс на базе нашего.

но основная проблема всех этих «наследований» — потенциальные конфликты интерфейсов. поэтому требуется обеспечивать глобальную уникальнойсть имён методов. у агрегации с этим делом по проще ( obj.width.enlarge() или obj.height.enlarge() вместо obj.enlarge() ), но проблема решается лишь частично…
так как звучит основное отличие примеси от штриха (подмножества примеси)? Как я понял, примеси в своих методах могут использовать данные, не описанные в самой примеси — т. е. данные из класса, который будет использовать эту примесь. Так? (хотя в определении написано, что примесь не содержит данных — или имеются в виду «свои данные»?)

Абстрактно:

Примесь использует переменную «a», которая не определена в ней:

класс МояПримесь:
  
  функция проверить():
    @a > 10 ? ложь : истина




Но «а» определена в классе, использующем примесь:

класс МойКласс:

  включить: МояПримесь

  инициализация:
    @a = 10
  



В итоге объекту класса «МойКласс» будет доступен метод «проверить» относительно «а».

Это примесь. А штрих тогда кто? Тот, кто не содержит «своих данных», плюс к этому даже не оперирует «свободными данными» (которые описаны в классе, использующем штрих/примесь)? Т. е. как в примере с Comparable из Ruby — описывается одна операция <=> в самом классе (т. е. данные не выходят наружу), а дальше примесь (штрих) уже на основе этой операции использует другие операции (но тем самым мы не хардкодим свободные переменные для примеси). Да?

P.S.: а вообще, день не прошел зря, что-то новое узнал. Спасибо =)
> так как звучит основное отличие примеси от штриха

а, ну да, именно в этом:

> описывается… операция… в самом классе (т. е. данные не выходят наружу), а дальше примесь (штрих) уже на основе этой операции использует другие операции

Штрих имеет доступ к методам класса, но не к данным. Но, надо полагать, что лишь к методам, которые класс реализовывает от штриха, а не ко всем (на уровне соглашения), иначе — получится, хоть и не нарушение инкапсуляции (поскольку методы — public), но хардкод чужих методов в примеси, а не свойств.
у каждого штриха есть зависимости — набор интерфейсов, которые класс должен реализовывать, чтобы методы штриха вообще могли работать.
Из мира SELF-a:

«A traits object is a parent object containing shared behavior, playing a role somewhat similar to a class in a class-based system. <...> Like traits objects, mixin objects encapsulate a bundle of shared behavior. Unlike traits objects, however, mixin objects are generally parentless to allow their behavior to be added to an object without necessarily also adding unwanted behavior.»

Спроецировав на Javascript, можно смело сказать, что traits — это prototype object, но менее смело, что mixin отсутствует…

Хм… Что-то я сбился. Значит трейтс (во всяком случае в SELF'е) — это обязательно родительский объект? А миксин — это тоже самое, но «без родителя» (?) (я правильно перевел — «parentless»? или «не родительский объект»?), что позволяет, так же расширить функционал сторонним поведением, но в отличии от traits избежать ненужных методов из родителя?
В traits заложена основная концепция «класса», общие признаки (штрихи), иерархия. Mixin сам по себе, ни от кого не зависит, вне иерархии, уточняет других. В Javascript концепция задаётся прототипами с их принципом делегирования, которые по сути и есть traits, где мы определяем общее поведение и т. д., mixin-ы же можно или считать отсутствующими, или это тупо любой объект, свойства которого можно подбросить (например, тем же копированием) к прототипу/объекту…
Ну да, только меня смутило обязательное «parent object». Вот все-таки — это только в SELF'e или это везде так относительно traits?

В весьма скудном описании Trait_(abstract_type) из en-википедии вообще какое-то очень странное отличие дано от mixin'ов:

Traits are similar to mixins, but may include definitions for class methods.


Что знаит, «но только может включать определения для классовых методов»? А что, примесь не может что ли? — Тоже может. Или имеется в виду определения методов, которые должны быть реализованы в классе? Так и примесь под это определение подходит.

В общем, вот PDF нашел: www.iam.unibe.ch/~scg/Archive/PhD/schaerli-phd.pdf

Цитата из главы 3.1 «Traits – Composable Units of Behavior»:

Traits bear a superficial resemblance to mixins, with several important differences. Several traits can be
applied to a class in a single operation, whereas mixins must be applied one at a time. Trait composition is
unordered, thus avoiding problems due to linearization of mixins. Traits contain methods, but no state, so state
conflicts are avoided, but method conflicts may exist. A class is specified by composing a superclass with a
set of traits and some glue methods. Glue methods are defined in the class and they connect the traits together;
i.e., they implement required trait methods (possibly by accessing state), they adapt provided trait methods, and
they resolve method conflicts.


> которые по сути и есть traits, где мы определяем общее поведение

да, абстрактную параллель (особенно из определения SELF'a) можно провести, только прототип может содержать и состояние, а не только методы;

> mixin-ы же можно или считать отсутствующими, или это тупо любой объект, свойства которого можно подбросить (например, тем же копированием) к прототипу/объекту

конечно, согласно текущей спецификации нет в ES никаких mixin'ов, но а, опять же абстрактно, — почему тупо? Вполне — обычное расширение объекта копированием.
и еще выдержка из другого PDF: www.iam.unibe.ch/~scg/Archive/Papers/Scha03aTraits.pdf

Traits have the following properties.

– A trait provides a set of methods that implement behaviour.

– A trait requires a set of methods that serve as parameters for the provided behaviour.

– Traits do not specify any state variables, and the methods provided by traits never access state variables directly.

– Classes and traits can be composed from other traits, but the composition order is irrelevant. Conflicting methods must be explicitly resolved.

– Trait composition does not affect the semantics of a class: the meaning of the class is the same as it would be if all of the methods obtained from the trait(s) were defined directly in the class.

– Similarly, trait composition does not affect the semantics of a trait: a composite trait is equivalent to a flattened trait containing the same methods.
>это только в SELF'e или это везде так относительно traits?
в SELF-e, откуда и растут ноги Javascript (в части делегирования со связыванием self), я бы других аналогий из других языков не искал, а то ещё сложнее станет спроецировать.

> почему тупо?
Брутальный синоним «прямо» ;) Я имел в виду, что если traits object из SELF ещё можно легко узнать в прототипе из Javascript, то mixin можно только предположить.

>только прототип может содержать и состояние, а не только методы
не принципиально это, прототип с тем же успехом может и НЕ держать состояние, оставив это объекту, тут важна общая для прототипа и для traits идея — прилинкованный «родитель» и делегирование…
> я бы других аналогий из других языков не искал, а то ещё сложнее станет спроецировать

да нет, я вообще не знал, что такое «штрих», поэтому интересно в любом случае — независимо от JS. А касательно JS я знаю, что терминология ES не описывает подобной сущности, поэтому (лично для меня) путаницы не будет.

> Брутальный синоним «прямо» ;) Я имел в виду…

да я понял =))) я тоже сказал, что «почему не смело? обычное расширение объекта новыми слотами — есть своего рода примесь».

Anyway, спасибо =)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории