Вы выбрали неудачную позицию — не анализирующую со стороны, а отстаивающую JS. Смотрите профессиональней, оценивайте, видьте плюсы и минусы. JS написали не Вы, а B. Eich, который заимстовал идеи ещё у кого-то. Он (B. Eich) может, ещё и может холиворить с Guido Van Rossum-ом =) но, уж явно не будет этого делать ;)
Почему? Потому что там терминология «класс», а там — «прототип»? Это не существенно. Важным атрибутами для анализа являются «динамика vs. статика» + механизм разрешения характеристик объектов. В Python и JS — они практически идентичны.
> но сами же указываете в статье их различия
Я-то указвыаю. Просто вы сказали, что «не очень корректно сравнивать», думал, тоже укажете на какую-то информацию и внесёте ясность. Ну, да ладно.
> Понятия «нормальное» наследования не существует. В реализации JS есть свои плюсы и минусы
Так в том-то и дело, что JS полностью и насквозь пронизан наследованием ещё до того, как мы напишем сколь-нибудь серьёзные объекты.
alert(1..toString());
Как только вы полностью поймёте эту строку, вопрос о наследовании отпадёт сам собой. И вы увидите, что наследование в JS мало чем отличается от наследования в Python.
Зрите в корень. У JS сейчас ниша занята определённая. При преносе этой нише в то же русло, что и Ruby — дополнительный функционал (соекты, файлы, многопточность и т.д.) допишутся в движок очень быстро. Более того, многие реализации этот функционал имеют (скачайте и скомпиллируйте, например, SpiderMonkey с поддержкой объекта File и ещё много чем).
> При создании экземпляров копируются методы и свойства, что сильно засоряет память и бьёт по производительности
Не возражаете, если я назову это бредом? ;)
— изначально нет никакой найтивной поддержки модульности и нормального наследования, что вполне объясняется предназначением языка — «небольшие скрипты улучшения интерфейса на клиентской стороне»
Являются-являются. В Ruby — подмешивание (ссылки на модули, к которым происходит делегация — поменяйте в рантайме значение в модуле, и оно тут же отобразится на всех объектах, которые подмешали эту примесь), в JS — расширение и один прототип для делегации (но, к слову сказать, в некоторых реализациях можно задействовать __noSuchMethod__ и делегировать по альтернативным цепям прототипов, т.е., по сути, организовывать множественное наследование или подмешивание).
> На мой взгляд это без труда обходится с помощью прототипов.
А в JS в качестве прототипа может быть указан лишь один объект (в отличии, скажем, от языка Self, от которого JS также вобрал немало идей). Примиси же, в одной из своих особенностей, позиционируются как альтернатива множественному наследованию, т.е. объект может подмешать несколько объектов и делегировать к ним. И, повторю, подмешивание и расширение — это разные вещи. Подмешивая, мы, по сути, создаём ещё один прототип для делегации, а расширяя — просто копируем свойства в объект. Почитайте ещё раз ссылку, которую я давал в предыдущем посте.
> Можете привести «живой» пример примеси?
class Age
include Comparable
attr_accessor(:age)
def <=>(cmp)
@age <=> cmp.age
end
end
a, b = Age.new, Age.new
a.age = 10
b.age = 11
a < b # true
Данная примесь, кстати, больше относится к штриху.
А Вас что именно интересует? Давайте и Вы пример приведите.
> А чем это плохо? Что ссылки вида «this.» отъезжают?
При чём здесь плохо? В Ruby — всего лишь — своя реализация. В JS — всего лишь своя реализация. Ни больше, ни меньше.
> Вот я и пытался выявить что-то эдакое, чего Javascript не сможет ну никак выжать.
Если видите большой смысл — почему бы не повыяснять? А вообще, тогда надо было спрашивать, «вот чего такого нет в Python-e или Self, или SmallTlak, или… (возьмите любой язык, от которого JS питал идеи), чего надо было делать в JS?». Ответы, кстати, найдёте, поскольку JS базируясь на определённых идеях, вносил и свои особенности. Так же и другие языки.
> То бишь JS их особо не отличает от других объектов, поэтому и не содержит большого числа специальных конструкций
Строки в JS — это примитивный тип. Объектный тип — только один — Object (не путать с конструктором Object!). Встроенный же конструктор String, создаёт объект типа Object, ставит его внутреннее свойство [[Class]] в «String» и записывает во внутреннее свойство [[value]] соответствующее примитивное значение, т.е. строку.
> Насчет примесей, все-таки прототипы и классы не очень корректно сравнивать.
Да, я знаю, что такое класс, и что такое прототип. Опять же, писал и разбирал это подробно в статье об ООП в JS. Почитайте, особенно раздел о динамично-классовой организации.
А что значит не «очень корректно»? А как «очень корректно» будет? Расскажите, интересно.
> Извеняюсь за занудство:) Ни неразу не встечал «hidden». SingletonClass или MetaClass.
Да, в Ruby принята эта терминология, для объектов создаётся SingletonClass, для классов — MetaClass. Однако, обе эти сущности являются терминологическим подмножеством VirtualHiddenClass. При этом, терминология MetaClass, применённая в Ruby, может сбить с толку в классическом понимании этого термина. Хотя, как часть — класс содержащий методы другого класса — можно назвать и метаклассом, как это сделано в Ruby.
Примесей, в понимании Ruby, в JS нет; ECMA-262-3 понятия «примесь» не описывает. Расширение объектов, как имитация, является имитацией, поскольку в Ruby, создаётся ссылка на модуль, а не просто копируются все свойство в расширяемый объект. Вот здесь немного писал об этом.
> замыкания с полной привязкой к переменным
В JS this не замыкается, а определяется динамически выражением вызова при входе в контекст. В Ruby self замыкается.
> В Ruby… можно добавлять методы не только в любые классы, но и в любые объекты
Кстати, не совсем верно, относительно Ruby. Там создаётся hidden-класс для каждого объекта, который и хранит методы. В JS — объекты полностью изменяемы (mutable) и могут сами хранить методы.
> покажите мне как сделать eval()
eval — вообще основная функция (всех) скриптовых языков.
> Есть ли там null, обладающий особыми свойствами?
А что за особые свойства? null-ы, nil-ы, None-ы есть во многих языках.
> Могу ли я работать с переданными аргументами любой функции как с массивом arguments?
Да, выше уже был пример на Python-e. Однако, в отличии от JS, можно анализировать не только позиционные аргументы, но и именованные:
> Есть Javascript. Зачем нужны другие скриптовые языки?
Время никого и ничего не щадит. Это как, если бы сказали в советские времена: «Да-а, какой шикарный автомобиль — Москвич 412! Лучше уже вряд ли что-нибудь придумают!» Какие-то новые идеи, архитектурные решения и т.д. появляются в процессе эволюции и развития. Мы можем лишь анализировать их, находить достоинства и недостатки, создавать собственные языки.
ECMAscript, кстати, очень много идей позаимствовал из Python-a. Они очень схожи в идеологии, опять же, в статье об ООП в JS, писал об этом.
> Что-то вы тут перебрали :) Xml читается куда легче, чем json, даже при наличии <>
Вопрос привычки. XML в отличии от JSON перегружен ненужными скобками, атрибутами с кавычками, знакми «равно» и т.д. Хотя, парсер для XML-подобных языков писать легче, он стандартизован и один на всех.
У меня коллега на работе, в качестве кандидатской писал нечто подобное. Только у него строилась матрица вероятностей нот. У него нет аккаунта на Хабре, но он сейчас посмотрел Ваш код и проект и сказал, что плюсом у Вас является анализ в дереве предыдущих нот, тогда как у него — следующая нота строится лишь в зависимости от текущей, но, зато, у него учитывается вероятности ноты появления ноты в оригинальном произведении (не просто рандомом берётся). Если вдруг заинтересуетесь, могу скинуть его аську.
> номер машины в виде штрих кода быстро был распознан сканером и ворота незамедлительно ушли под землю
Не практично, архитекторы бы не стали делать систему с уходом под землю и обратным подъёмом из-под земли — а вдруг, система заклинит, когда машина будет проезжать по ним, и ворота (глюком) выйдут из-под земли в этот момент? Наверное, ворота, всё же, раскрывались/закрывались, что, в случае аварии, более безопасно.
Автопилот, новости на бортовом компьютере и «зелёный сектор» (не важно чего, в данном случае, новостей) — было неоднократно и, да, ассоциируется с будущим.
Ага, пожалуй. Только этот private тоже до конца не скрыт (в некоторых реализациях можно свободно менять var-ы из замкнутого контекста). Вот здесь немного писал об этом.
> зачем свойство? просто переменная, доступная методам через замыкание, в котором сохранен контекст функции-конструктора.
Я просто подумал, что вы говорите про:
function A() {}
A.staticProperty = 10;
а не про:
function A() {
var counter = 0;
return function() {
alert(++counter);
};
}
Кстати, этот второй случай — лишь создание внутреннего замыкания; в качестве же конструктора такую функцию уже не использовать (т.к возвращается не объект и не this, а функция), поэтому, это вряд ли может подойти под определение static в плане расшаривания свойств между экземплярами конструктора. Да, переменная counter будет шарится между вызовами замкнутой анонимной функции (через [[Scope]]), но только это не относится к созданию экземпляров и расшаренного между ними («статического») свойства.
Если в качестве конструктора использовать эту возвращённую функцию — то тоже смысла мало, counter будет доступен только в пределах этого анонимного конструктора.
Ещё можно такой вариант использовать — хранить не в самом прототипе, в скопе обрамляющей функции при описании прототипа. Получится, что переменная также расшарена между всеми экзеплярами, но ещё и недоступна снаружи:
function A() {} // конструктор
A.prototype = (function () { // обрамляющий контекст
// расшаренное "статическое" свойство
var sharedProperty = 10;
// сам прототип
return {
constructor: A,
getSharedProperty: function () {
return sharedProperty;
},
setSharedProperty: function (newValue) {
sharedProperty = newValue;
}
};
})();
var a = new A;
var b = new A;
alert(a.getSharedProperty()); // 10
alert(b.getSharedProperty()); // 10
b.setSharedProperty(20);
alert(a.getSharedProperty()); // 20
alert(b.getSharedProperty()); // 20
Вы выбрали неудачную позицию — не анализирующую со стороны, а отстаивающую JS. Смотрите профессиональней, оценивайте, видьте плюсы и минусы. JS написали не Вы, а B. Eich, который заимстовал идеи ещё у кого-то. Он (B. Eich) может, ещё и может холиворить с Guido Van Rossum-ом =) но, уж явно не будет этого делать ;)
Почему? Потому что там терминология «класс», а там — «прототип»? Это не существенно. Важным атрибутами для анализа являются «динамика vs. статика» + механизм разрешения характеристик объектов. В Python и JS — они практически идентичны.
> но сами же указываете в статье их различия
Я-то указвыаю. Просто вы сказали, что «не очень корректно сравнивать», думал, тоже укажете на какую-то информацию и внесёте ясность. Ну, да ладно.
Какую ещё обычную?
> Понятия «нормальное» наследования не существует. В реализации JS есть свои плюсы и минусы
Так в том-то и дело, что JS полностью и насквозь пронизан наследованием ещё до того, как мы напишем сколь-нибудь серьёзные объекты.
Как только вы полностью поймёте эту строку, вопрос о наследовании отпадёт сам собой. И вы увидите, что наследование в JS мало чем отличается от наследования в Python.
> — ООП ужасно неудобное
Альтернативная парадигма. Скорее, Вам, «ужасно непривычная».
> При создании экземпляров копируются методы и свойства, что сильно засоряет память и бьёт по производительности
Не возражаете, если я назову это бредом? ;)
— изначально нет никакой найтивной поддержки модульности и нормального наследования, что вполне объясняется предназначением языка — «небольшие скрипты улучшения интерфейса на клиентской стороне»
Тоже, не против, если назову это ерундой? ;)
Являются-являются. В Ruby — подмешивание (ссылки на модули, к которым происходит делегация — поменяйте в рантайме значение в модуле, и оно тут же отобразится на всех объектах, которые подмешали эту примесь), в JS — расширение и один прототип для делегации (но, к слову сказать, в некоторых реализациях можно задействовать __noSuchMethod__ и делегировать по альтернативным цепям прототипов, т.е., по сути, организовывать множественное наследование или подмешивание).
А в JS в качестве прототипа может быть указан лишь один объект (в отличии, скажем, от языка Self, от которого JS также вобрал немало идей). Примиси же, в одной из своих особенностей, позиционируются как альтернатива множественному наследованию, т.е. объект может подмешать несколько объектов и делегировать к ним. И, повторю, подмешивание и расширение — это разные вещи. Подмешивая, мы, по сути, создаём ещё один прототип для делегации, а расширяя — просто копируем свойства в объект. Почитайте ещё раз ссылку, которую я давал в предыдущем посте.
> Можете привести «живой» пример примеси?
Данная примесь, кстати, больше относится к штриху.
А Вас что именно интересует? Давайте и Вы пример приведите.
> А чем это плохо? Что ссылки вида «this.» отъезжают?
При чём здесь плохо? В Ruby — всего лишь — своя реализация. В JS — всего лишь своя реализация. Ни больше, ни меньше.
> Вот я и пытался выявить что-то эдакое, чего Javascript не сможет ну никак выжать.
Если видите большой смысл — почему бы не повыяснять? А вообще, тогда надо было спрашивать, «вот чего такого нет в Python-e или Self, или SmallTlak, или… (возьмите любой язык, от которого JS питал идеи), чего надо было делать в JS?». Ответы, кстати, найдёте, поскольку JS базируясь на определённых идеях, вносил и свои особенности. Так же и другие языки.
Строки в JS — это примитивный тип. Объектный тип — только один — Object (не путать с конструктором Object!). Встроенный же конструктор String, создаёт объект типа Object, ставит его внутреннее свойство [[Class]] в «String» и записывает во внутреннее свойство [[value]] соответствующее примитивное значение, т.е. строку.
Да, я знаю, что такое класс, и что такое прототип. Опять же, писал и разбирал это подробно в статье об ООП в JS. Почитайте, особенно раздел о динамично-классовой организации.
А что значит не «очень корректно»? А как «очень корректно» будет? Расскажите, интересно.
> Извеняюсь за занудство:) Ни неразу не встечал «hidden». SingletonClass или MetaClass.
Да, в Ruby принята эта терминология, для объектов создаётся SingletonClass, для классов — MetaClass. Однако, обе эти сущности являются терминологическим подмножеством VirtualHiddenClass. При этом, терминология MetaClass, применённая в Ruby, может сбить с толку в классическом понимании этого термина. Хотя, как часть — класс содержащий методы другого класса — можно назвать и метаклассом, как это сделано в Ruby.
> «примеси»
Примесей, в понимании Ruby, в JS нет; ECMA-262-3 понятия «примесь» не описывает. Расширение объектов, как имитация, является имитацией, поскольку в Ruby, создаётся ссылка на модуль, а не просто копируются все свойство в расширяемый объект. Вот здесь немного писал об этом.
> замыкания с полной привязкой к переменным
В JS this не замыкается, а определяется динамически выражением вызова при входе в контекст. В Ruby self замыкается.
> В Ruby… можно добавлять методы не только в любые классы, но и в любые объекты
Кстати, не совсем верно, относительно Ruby. Там создаётся hidden-класс для каждого объекта, который и хранит методы. В JS — объекты полностью изменяемы (mutable) и могут сами хранить методы.
> покажите мне как сделать eval()
eval — вообще основная функция (всех) скриптовых языков.
> Есть ли там null, обладающий особыми свойствами?
А что за особые свойства? null-ы, nil-ы, None-ы есть во многих языках.
> Могу ли я работать с переданными аргументами любой функции как с массивом arguments?
Да, выше уже был пример на Python-e. Однако, в отличии от JS, можно анализировать не только позиционные аргументы, но и именованные:
В Ruby тоже свободно:
> Могу ли я создать в них объект с данными и методами, написав:
Да, в Python, можно даже использовать для этого литерал словаря (не создавая отдельный класс) — явное сходство с объектами в JS:
> Есть Javascript. Зачем нужны другие скриптовые языки?
Время никого и ничего не щадит. Это как, если бы сказали в советские времена: «Да-а, какой шикарный автомобиль — Москвич 412! Лучше уже вряд ли что-нибудь придумают!» Какие-то новые идеи, архитектурные решения и т.д. появляются в процессе эволюции и развития. Мы можем лишь анализировать их, находить достоинства и недостатки, создавать собственные языки.
ECMAscript, кстати, очень много идей позаимствовал из Python-a. Они очень схожи в идеологии, опять же, в статье об ООП в JS, писал об этом.
Среда и место исполнения — это мелочи, следствие.
> вообще не понимаю, как Вы можете сравнивать Javascript и Ruby
А что непонятного? Сравниваются сами языки. Кстати, JS, Python и Ruby стоят в одной линейке, у них схожие идеологии.
Ну, здрасте! ;) Язык ассемблера — это супер-мега абстракция (относительный сверхвысокий уровень), по сравнению с тем, что «было в начале».
a?
> Я же не спрашиваю
ну, тогда можете проигнорировать мой ответ. Я отметил абстрактно.
> Я указываю
да что Вы? ;)
> на то, что более-менее крупный json читается человеком гораздо сложнее, чем аналогичный xml; на это и указал.
локальная привычка, не более; как вариант, повторю, посмотрите в сторону YAML, тот ещё легче читается.
Вопрос привычки. XML в отличии от JSON перегружен ненужными скобками, атрибутами с кавычками, знакми «равно» и т.д. Хотя, парсер для XML-подобных языков писать легче, он стандартизован и один на всех.
JSON — легче по трафику для передачи.
Также, есть ещё YAML, который ещё легче JSON-a.
У меня коллега на работе, в качестве кандидатской писал нечто подобное. Только у него строилась матрица вероятностей нот. У него нет аккаунта на Хабре, но он сейчас посмотрел Ваш код и проект и сказал, что плюсом у Вас является анализ в дереве предыдущих нот, тогда как у него — следующая нота строится лишь в зависимости от текущей, но, зато, у него учитывается вероятности ноты появления ноты в оригинальном произведении (не просто рандомом берётся). Если вдруг заинтересуетесь, могу скинуть его аську.
Не практично, архитекторы бы не стали делать систему с уходом под землю и обратным подъёмом из-под земли — а вдруг, система заклинит, когда машина будет проезжать по ним, и ворота (глюком) выйдут из-под земли в этот момент? Наверное, ворота, всё же, раскрывались/закрывались, что, в случае аварии, более безопасно.
Автопилот, новости на бортовом компьютере и «зелёный сектор» (не важно чего, в данном случае, новостей) — было неоднократно и, да, ассоциируется с будущим.
Продолжайте, интересно ;)
Я просто подумал, что вы говорите про:
а не про:
Кстати, этот второй случай — лишь создание внутреннего замыкания; в качестве же конструктора такую функцию уже не использовать (т.к возвращается не объект и не this, а функция), поэтому, это вряд ли может подойти под определение static в плане расшаривания свойств между экземплярами конструктора. Да, переменная counter будет шарится между вызовами замкнутой анонимной функции (через [[Scope]]), но только это не относится к созданию экземпляров и расшаренного между ними («статического») свойства.
Если в качестве конструктора использовать эту возвращённую функцию — то тоже смысла мало, counter будет доступен только в пределах этого анонимного конструктора.
Ещё можно такой вариант использовать — хранить не в самом прототипе, в скопе обрамляющей функции при описании прототипа. Получится, что переменная также расшарена между всеми экзеплярами, но ещё и недоступна снаружи: