Pull to refresh

Comments 59

Даешь Pattern matching в Javascript! :)
Оригинально.
А на практике насколько это удобно?
Только начал использовать, пока доволен. Мы портируем Java-библиотеку на JS, а в ней есть классы с десятью конструкторами. Эта штука серьёзно облегчает жизнь :-)
От того, что у вас есть, остался 1 шаг по pattern matching-а.
На мой взгляд, это как раз апофигей полиморфности. Когда проверять можно не тип аргумента, но и его содержимое.
К сожалению, pattern matching с более-менее нетривиальными правилами и с приличной глубиной добавит ещё оверхеда, причём прилично, поскольку в JS сложно сделать эффективные итераторы. Я сейчас потихоньку экспериментирую с препроцессором, который будет встраивать вспомогательный код непосредственно в тело использующих его функций, тогда, может, будет нормально.
Вобщем да.
Тут уже вопрос надобности. Надо ли писать язык на языке.
Насколько я знаю, это естественный путь развития скриптовых языков — все они написаны на каком-то языке :)
нескриптовые, впрочем, тоже. За очень редким исключением. По крайне мере компиляторы си написаны на си :)
> А на практике насколько это удобно?
Менее удобно, чем реализация средствами языка, но уж гораздо лучше чем более часто используемый первый пример.

Если есть способ избежать подобных извращений, то лучше избежать… Но если очень хочется, то можно))))

Я бы посоветовал сделать в виде плагина jQuery.
Так было бы удобней подключать и использовать.
Я думал над этим, но это лишняя зависимость. В принципе это делается добавлением одной строчки в код, а так как лицензия MIT, то кто угодно может этим заняться :-) Возможно, добавлю в SVN jQuery-версию дополнительно к обычной.
Уж чегочего но это точно не для jQuery.
Вы вообще в курсе, что javascript есть не только в браузере?
хм… сорри. Это был ответ на верхний коммент.
Я бы еще спросил «Вы вообще в курсе, что на javascript можно программировать без jQuery?».
Зачастую считается что нельзя :)
Может для библиотеки это хороший прием, но не для конечного кода — однозначно. Код получается не отражающим свою сущность, хотя интерфейс выходит удобным.

А может можно увеличить быстродействие с помощью прототипов, или, скажем расширив класс функции через прототип?
Можно попытаться сгенерировать тело новой функции, активно используя toString и new Function. А как прототип класса функции может увеличить быстродействие? Можно пример?
Я рассуждаю гипотетически. К сожалению с необходимостью перегрузки функций в JS ни разу не сталкивался.

Упоминая прототипы — я говорил о том, чтоб расширить Function, добавив туда возможность перегружать методы. Мне кажется это может работать быстрее.

Если будет время — поковыряюсь, может что-то и придумаю. Или обнаружу что я дико ошибся, что тоже познавательно.
Ага… не по тем ключевым словам искал. Спасибо. Ну хоть контроля типов там нет :-)
хмммм… что-то мне подсказывает что так можно сделать и в PHP5.3, зачем не спрашивайте
Перегрузка не является стандартным решением для JS. Статья не может считаться законченной без предупреждения: Подобная практика приводит к трудночитаемости кода и в условиях работы в команде разработчиков грозит физическими побоями, используйте на свой страх и риск, только в крайних случаях.
Аккуратное решение=) спасибо что поделились!
Вот интересно, за что меня проминусовал, тот человек который это сделал?)

Я честно честно не буду п*здить печеньки лучшего решения, чем это не знаю=)

Я стараюсь во все более-менее сложные функции пихать не набор параметров, а объект параметров. Не так «по-взрослому», но вполне удобно (бесконечно расширяемо и обратно совместимо).
почему не по-взрослому? Фаулер, кстати, рекомендует :)
1) Решение — изврат
2) Полиморфные функции в общем-то зло. Почему? Да потому что, раз они называются одинаково, то и код у них должен быть одинаковый, а так появляется путаница.
У них семантика должна быть одинаковая. А интерфейс может быть разный. Это удобно. Нередко все перегруженные функции вызывают одну из них, которая реализует фактическую функциональность.
Считаю, все же лучше ограничиться параметрами по умолчанию и (возможно) проверкой типа, но все же написать одну функцию, а не несколько. К тому же, эти функции будут содержать похожий код, что плохо.
есть замечательный практический пример. Нужно найти расстояние от точки до первой точки плюс от второй для третьей и так далее.

Зачастую, когда точек немного и они получаются операцией с точками, удобно использовать в качестве параметров два-три объекта с координатами точек.

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

В данном случае никто не мешает внутри перегруженной функции вызывать самою себя. Работа перезгрузки заключается лишь в приведении аргументов к общему виду.
Гм, тут лучше назвать 2 функции по-разному, так как логика их действия по моему немного различается. И в данном примере — отличный пример двусмысленности, которая порождает ошибки и трудности с отладкой. Куда проще тут сделать distanceForObjects(...) и distanceForArray(...)

Еще, кстати один пример неудачного, имхо, полиморфизма — это когда функцию вроде max() можно вызвать с массивом аргументов: max([1,2, 3]), а можно — с несколькими числовым аргументами: max(1,2,3) На мой взгляд, выигрыш от экономии 2 скобок не перекрывает возмодные сложности с пониманием кода.
> max(1,2,3)… max([1, 2, 3])
просто доп. гибкость. например, когда числа получаем из какой-нибудь фукнции массивом, не нужно apply делать — удобно, если формат возвращаемого значения от этого не меняется.
лучше так:
var polyFunc= polymorph(function( i /*Number*/, str /*String*/ ){
})
Не сработает в Gecko, пытался. Он выкусывает комментарии в .toString (стандарт не регламентирует, можно ли это делать).
зачем же такие сложности, когда можно передать в качестве параметра объект? :)
А конструировать объект тоже объектом? У вас в каждой функции один параметр? :-)
а зачем в каждую функцию один параметр? только в «полиморфные» :)
эм, туго соображаю под вечер — возможно, не совсем понял вопрос…

но что мешает сделать как-нибудь так:

function getRectangleArea(opt) {
return (opt.x2 — opt.x1) * (opt.y2 — opt.y1) || (opt.width * opt.height)
}
UFO just landed and posted this here
Применительно к рассмотренному случаю, я бы написал так:

function getRectangleArea(/**Object*/o)/**Number*/{
  function getRectangleArea(/**Number*/width, /**Number*/height)/**Number*/{
    return width * height;
  }
  if ('width' in o && 'height' in o) {
    return getRectangleArea(o.width, o.height);
  }
  if ('x1' in o && 'x2' in o && 'y1' in o && 'y2' in o) {
    return getRectangleArea(o.x2 - o.x1, o.y2 - o.y1);
  }
}
console.log(getRectangleArea({width: 10, height: 10}));
console.log(getRectangleArea({x1: 10, x2: 20, y1: 10, y2: 20}));

* This source code was highlighted with Source Code Highlighter.
помоему стоит разделить функцию polimorph на 2, т.е. реализацию отделить от заполнения функциями
тогда: 1 будет понятней работа, 2: можно будет динамически добавлять функции/объекты
И так можно. Пример есть по ссылке, я в статье не написал, чтобы статья вышла покороче. В принципе вы можете написать:
var getRectangleArea3 = polymorph();

getRectangleArea3.update(
  function(width, height) {
    return width * height;
  },
  function(x1, y1, x2, y2) {
    return (x2-x1)*(y2-y1);
  }
);

* This source code was highlighted with Source Code Highlighter.

Работает точно так же.
Конечно если вы мигряетесь с явы, то это полезно, но вообще с точки зрения JS абсолютно не надо. Во-первых, ни одна IDE вам таким образом не подскажет список параметров. Во-вторых, в отличие от Java в JS есть JSON, что позволяет передавать параметры в виде обьектов весьма удобным способом:

myFunc({width: 100, height: 200})
myFunc({x1: 10, y1: 10, x2: 300, y2: 240})

Ну а в-третьих, JS-кодеры со стажем по голове дадут (: И за перформанс и за мясо.
А при JSON-передаче данных редакторы подскажут список параметров?

В моём случае, кстати, редакторам будет несложно подтянуться, если вдруг штука станет популярной :-) А с JSON вообще непонятно. Писать перед функцией комментарий в определённом формате, чтобы редактор узнал возможные прототипы функции?

А с перформансом, мне кажется, многие не заморачиваются. Вот тестировал на днях jQuery-inheritance. Там на вызов метода, который вызывает себя же в базовом классе (this.__base(...)), оверхед примерно такой же, как у меня (по сравнению со стандартной некрасивой записью типа BaseClass.prototype..call(this, ...)). То есть чисто красота вызова метода базового класса стоит тех же микросекунд. И ничего, люди пользуют и хвалят :-)
> BaseClass.prototype..call(this, ...)
Галочки Хабр скушал. Следует читать:
BaseClass.prototype.<MethodName>.call(this, ...)
С параметрами штука в том, что в дев-версии скриптов многие пишут JSDoc и большинство IDE понимает JSDoc. Проблема JSDoc в том, что в данный момент он не расчитан на перегруженные функции, а вот указать формат обьекта и полноценно задокументировать можно (: Конечно если JSDoc подтянется, то такие перегрузки будут иметь смысл, но это врядли.

Зачем вообще делать аннотации через JSDoc? Потому, что JS язык слабо-типизированный и IDE сама по себе не может определить что вы ей вернёте. Поэтому все вменяемые люди им пользуются.

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

Впрочем, данный плагин к самой jQuery никакого отношения не имеет и мог бы существовать без неё.

> вот вы тоже пытаетесь JS превратить в Java, но зачем?
Я не пурист, а практик. Есть много готового кода, который внезапно захотелось выполнять на клиенте (java-апплет не надо советовать). Перенос с сохранением Java-концепций и исходных интерфейсов выполняется быстро и почти автоматически. Пока мы будем писать аналогичный, но концептуально прекрасный JS-код, конкуренты уйдут вперёд :-)
Ну так я сразу написал: Конечно если вы мигряетесь с явы, то это полезно. Никто не спорит, что в таких ситуациях важнее быстро смигряться. Но потом когда будет время постарайтесь уделить время JS в виде JS (:
Чуточку не в тему, но про похожий мехнизм.

Я однажды очень расстроился когда не нашел в ЖС механизмов геттеров и сеттров для объектов, которые позволили бы мне делать подобную штуку с объектами
__defineGetter__ и __defineSetter__ были реализованы в движке SpiderMonkey, который используется в firefox (теперь уже TraceMonkey) а также в движке V8 (google chrome)
А остальные движки?
ну, скриптовые сеттеры и геттеры это не обычные сеттеры и геттеры, т.к. они не привязаны к переменной, а именованные «несвойства» объекта
не суть важно. Прототип в жаваскрипте тоже можно назвать несвойством. Здесь больше вопрос удобства, бо в жаваскрипте вопрос быстродействия на уровне обращения к структурам данных обычно не стоит. Быстродействие загибается в момент взаимодействия с дом-моделью.
Ну очень хочется заметить самое ГЛАВНОЕ. Вы говорите о ПОЛИМОРФИЗМЕ? А реализуете перегрузку (overloading). Тот кто захочет найти реализацию полиморфизма в вашем коде, зря потратит время. Вам их не жалко? А если они еще не знают о существовании полиморфизма (одном из 4 главнейших механизмов!!! ООП). Получится как в истории про молодого человека, который был уверен, что точка G («же») его подруги находится в «ж»опе.

И за красоту надо платить только в том случае, если вы хотите ее купить :)

Для эмигрантов явы, можно посоветовать GWT. Если нужна более близкая объектная реализация (притягивание) JS присмотритесь к Prototype.
Прежде чем чем говорить о термине, удостоверьтесь, что вы знаете его общее значение.

В данном случае перегрузка (overloading) является видом полиморфизма.

ООП тут не причем.
Полиморфизм есть и в ML и даже в С.
Случай обычный, перегрузка это перегрузка, полиморфизм это полиморфизм :) кто из них вид, класс, отряд, подвид, частный случай конечно вам решать. В реализации языка JS нет «Полиморфизма функций». Вы эмулировали и создали случай, не вводите в заблуждение людей. Коли речь идет о параметрическом полиморфизме, встречающемся в функциональном программировании, не путайте только и не говорите потом, что функция JS является видом, случаем, частным отголоском функционального программирования, якобы слова однокоренные и есть общий термин «функция», то так и пишите :)
Во первых я не автор топика, поэтому ничего не эмулировал.
Во-вторых параметрический полиморфизм (например шаблоны С++), перегрузка функций и неявное приведение типов является полиморфизмом в той же степени как и иерархический полиморфизм в ООП (механизм виртуальных функций).
Переходя на метафоры
Просто я заметил, что JS — это язык программирования.
Вы же утверждаете, что язык программирования это язык программирования, а JS это JS.

я говорил про общий случай.
In computer science, polymorphism is a programming language feature that allows values of different data types to be handled using a uniform interface. [from wiki]

вы про частный:
Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented programming, is the ability of one type, A, to appear as and be used like another type, B. [wiki]
В целом согласен, хотя это всё равно полиморфизм, как отметил zvulon. Изменил «полиморфизм» на «перегрузку» в названии и в тексте один раз и добавил тегов. В коде уж менять не буду, пусть остаётся :-)
Sign up to leave a comment.

Articles