jQuery - прекрасный инструмент. В нем решены многие задачи, с которыми web-программист сталкивается изо дня в день. Долгое время одним из недостатков jQuery его приверженцами называлось отсутствие правильного объектно-ориентированного подхода. Если разработчику нужно было создать собственный класс, то это приходилось делать стандартными средствами JS. И выглядело это весьма причудливо. Чтобы описать собственный класс приходилось использовать следующую конструкцию.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // Объявление класса function Car (vendor, color) { this.vendor = vendor; this.color = color; this.speed = 0; this.setNewSpeed = setSpeed; } function setSpeed(speed) { this.speed = speed; } // Объявление объекта класса var newCar = new Car ('BMW', 'red'); // Вызов метода newCar.setNewSpeed(150); // Отобразим значение свойства alert(newCar.speed); // Создание потомка function Trailer (vendor, color, tonnage) { Car.call(this, vendor, color); this.tonnage = tonnage; } // Создание экземпляра var newTrailer = new Trailer('Daewoo', 'yellow', 30); |
Как видите мы создали функцию function Car, которая называется конструктором. Затем мы создали функцию function setSpeed и одному из свойств присвоили указатель на нее this.setNewSpeed = setSpeed; Конечно же сложно назвать это правильным ООП. Однако пока во всех браузерах не появилась поддержка JavaScript 2, мы не имеем возможности использовать ключевое слово class.
Однако John Resig (разработчик jQuery) дополнил jQuery модулем Сlassy Query. Это позволяет нам создавать классы с уже более близким к нормальному ООП синтаксисом. Те разработчики, которые имели знакомство с библиотекой prototype.js увидят знакомую конструкцию.
1 2 3 4 5 6 7 8 9 10 11 12 | // Объявление класса Person var Person = jQuery.Class.create({ init: function(name){ this.name = name; } }); // Создание класса ClassyDude как потомка Person var ClassyDude = Person.extend({ sipWine: function(){ return "Has a beautiful bouquet."; } }); |
Глянув на такой код сразу будет видно, что создаются классы, а не просто функции. Такой код более читаем и удобен в написании.
Теперь я расскажу об еще одной маленькой проблеме. Проблема состоит в изменении контекста this при прикреплении обработчика к событию. Продемонстрирую проблему.
1 | <input type="button" id="mainButton" value="Click me"/> |
1 2 3 4 5 6 7 8 9 10 11 12 | // Объявление класса mainButtonClass var mainButtonClass = jQuery.Class.create({ init: function(id){ this.button = $('#'+id)[0]; this.valurAfterClick = "Don\'t click me!"; $(this.button).bind("click", this.changeButton); }, changeButton:function() { alert(this.valurAfterClick); } }); new mainButtonClass('mainButton'); |
Здесь произведена попытка задать кнопке в качестве обработчика на нажатие метода класса. Однако alert будет показывать надпись undefined. А происходит это потому, что при прикреплении событию функции-обработчика, внутри этой функции происходит изменения контекста this. И this становится указателем на саму кнопку. Вот такая проблема. Так вот в jQuey есть способ это обойти.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var mainButtonClass = jQuery.Class.create({ init: function(id){ this.button = $('#'+id)[0]; this.valurAfterClick = "Don\'t click me!"; $(this.button).bind("click", {thisObj:this, element: this.button}, function(eventData) { eventData.data.thisObj.changeButton(eventData.data.element) }); }, changeButton:function(element) { element.value = this.valurAfterClick; element.disabled = true; alert(this.valurAfterClick); } }); new mainButtonClass('mainButton'); |
При таком коде наш пример будет работать корректно. Теперь мы в самом деле можем использовать ООП в JS-разработке. Не хватает конечно же еще спецификаторов доступа (private, public). Но наверное это максимум, что мы можем получить пока не будет в нашем распоряжении JavaScript 2. Удачи вам в применении новых подходов!
Tags: jquery
Я предпочитаю в js создавать объекты через фабрику объектов. Это выглядит так:
тоже конечно не сахар, но благодаря замыканиям можно приватные переменные использовать.
> Однако John Resig (разработчик jQuery) дополнил jQuery модулем Сlassy Query.
// Happy April Fools Day 2008
// The code is good - read for inspiration, but
// please don’t use this
classy.js
> Это позволяет нам создавать классы с уже более близким к нормальному ООП синтаксисом
если хотите глубже понимать JS - глубже изучайте JS; он и так ООП-язык, только с альтернативной, прототипной парадигмой, которая имеет свои плюсы и минусы относительно классовой парадигмы.
Bjaka.max, большое спасибо за демонстрирование интересного трюка!
Я правда не знал его. Немного погуглил на тему замыканий. Напишу что накопал (для читателей блога).
Если для одной функции создать вложенную
То во внутренней функции будут доступны переменные внешней. И интересно, что эти переменные остаются доступными даже после того, как внешняя функция была выполнена.
Это и позволяет нам создавать псевдо-приватные свойства объектов.
Правда сразу вижу один недостаток. Эти свойства придется писать без this. Что во-первых будет выглядеть как отсутствие единого стиля обращения к свойствам объекта внутри методов, а во-вторых при опечатке в имени переменной переменная попадет в глобальную область и перестанет быть частью объекта так же как и приватной переменной. Да, кстати, в третьих… Если вдруг разработчик захочет сделать public-свойство приватным, то ему придется везде удалять приписку this.
To Dmitry A. Soshnikov
Что мы и делаем. Не стреляйте в пианиста - он играет как может
Да да, я в курсе, что создатель сего сам не рекомендует пока использовать classy. Однако аргументирует он это недостаточной еще проработанностью classy. Но судя по тому, что prototype.js уже давно использует точно такой подход, то в итоге classy станет рекомендацией уже в недалеком будущем.
Если верить википедии, то вы правы. Там написано:
Однако похоже прототипы менее распространены, чем классы. Наверняка в силу веских причин.
Интересно было бы узнать эти плюсы и минусы.
Во-первых, динамика (хотя в динамических классовых языках, типа Python и Ruby, она тоже присутствует). Т.е. можно в рантайме расширять объекты (поскольку они все mutable-объекты), прототипы объектов (и это тут же отобразиться на все уже порожденных инстансах).
Если прототипная модель является делегирующей (есть еще и каскадная; wiki), то прототип хранит всегда одну копию свойства (все инстансы делегируют к ней) - таким образом, экономятся ресурсы (но - увеличивается время).
Касаемо же всех оберток в виде классов (типо classy.js) - стоит понимать, что внутри этих оберток - ровно то же самое, что я написал выше. Никаких статических классов там нет (и быть не может). Однако, эти обертки могут быть удобны для организации наследования, просто нужно иметь в виду, что внутри этих оберток - никакой магии нет, это просто синтаксический сахар, содержащий ровно то, что написана выше.
И что реально пригождается? Мне не разу не пригодилась эта особенность. Да и более читаем код, если сначала описать классы, а потом уже использовать их функционал.
Думаю, не стоит недооценивать окружающих. Вряд ли кто-то думал, что там магия. Тем более что код открыт.
Ну а разница какая - опишите Вы без обертки или с оберткой?
Ни в коем разе не недооценивал никого здесь. Просто сначала приводится тоже самое, а потом обертка.
При этом удобство любой подобной обертки лишь одно - упростить блок, обеспечивающий связку прототипов для обеспечения наследования.
Ммм… Думаю, читаемость кода при использовании оберток лучше… Отсюда меньше ошибок, меньше времени на отладку, дешевле трудозатраты.
Да, в большей мере, здесь все завязано лишь на локальные привычки/непривычки. Однако, говорить, что:
в корне не верно, и может нести безграмотность в массы. А использовать обертки или нет - каждый решает сам, исходя из удобства (у меня и у самого были написаны некоторые вариации подобных оберток) и привычек; главное, повторю - не пропагандировать эти обертки, как “то, что позволит довести до ума JavaScript, который сложно назвать ООП-языком”, а представлять, как просто удобную обертку.
Это каскадная реализация - когда все свойства и методы копируются в объект от прототипного объекта (в данном случае - от объекта, порожденного (возвращенного) конструктором). Основной недостаток - каждый порожденный объект будет иметь свои собственные одинаковые методы (например, getSpeed), и памяти это будет расходовать намного больше - прямо пропорционально количеству объектов. В то время, как, если бы, этот метод был вынесен в прототип - он был бы одни на все инстансы (правда, здесь, увеличилось бы время доступа, поскольку метод бы искался в цепи прототипов).
Да да. И большинство программистов привыкли как раз к классовой парадигме. И тот факт, что вы собственные обертки пишите лишь говорит о том, что на голом JS не очень то комфортно.
Дело в терминах и не более. Я просто не знал что прототипы тоже относятся к ООП. Да и не я один. Масса людей знают как пользоваться prototype и иже с ними. Однако не все знают, как вы, что это ООП. Я например слышал как люди называют это ПОП (прототипо ориентированное). Однако это всего-лишь термин. А обертки экономят деньги, так как экономят время.
Вы кстати вот не ответили пригодилось ли вам это прототипирование хоть раз? Мне - нет. Если пригодилось то для чего? И почему в этом случае на классах ну никак нельзя было? А так же в чем положительная сторона написания кода без использования оберток? Неужели это умение ради умения? Практическая ценность есть?
Дело в терминах и не более.
Более, более. Дело в другой идеологии. Хотя, к примеру, классовая модель Python’a очень похожа (за исключением небольших нюансов) на делегирующую прототипную модель JavaScript’a.
Слово “другой” наиболее хорошо видно, когда речь идет о статической классовой организации. Динамическая (как Python, Ruby и т.д.), повторю, похожа.
Вы кстати вот не ответили пригодилось ли вам это прототипирование хоть раз? Мне - нет. Если пригодилось то для чего?
Приходилось. И Вам приходилось. Для обеспечения наследования. Как вариант, можно попросить объяснить, что происходит в следующей конструкции:
Что значит, “никак нельзя было”? Просто классов нет в JS. Однако, есть связка “конструктор + прототип”; при этом, прототип - является хранилищем и в кавычках может рассматриваться, как класс.
Смотря, что Вы пишите и для чего. Обертка внутри себя может содержать тонну тяжеловесного кода, тогда как использование прикладной сущности, созданной с помощью этой обертки, могло бы быть и без нее (обертки) - и в 100 раз быстрее.
Если же видно, что некоторые куски кода повторяются многократно, то пишутся блоки, куда этот код выносится (и данные обертки - всего лишь такие же куски кода, которые содержат внутри себя повторяющиеся блоки). Естественно, code reuse улучшается.
Да нет, это уже другой вопрос. Я всего лишь подкорректировал Вас в утверждении “Конечно же сложно назвать это правильным ООП.”, не более. Как воспринимать эту информацию - Ваше право И это не “умение ради умения”, это более точное описание технологии, согласно ее стандарту.
Я уже в который раз даю понять, что очень много людей (включая меня) знают про прототипирование, знают, что с помощью них можно наследовать, но просто не знают что это тоже ООП. Поэтому вопрос как раз в терминологии. И не более.
Вот в том то и дело, что я не использовал никогда прототипирование напрямую. Я использовал обертки, которые давали мне иллюзию наличия классовой парадигмы. Поэтому я считаю, что именно прототипный подход мне не пригодился ни разу. И смысла в нем особого нет.
Вот именно. А если бы были? Получается что люди пользуются этим прототипированием только потому что другого нет. Однако на деле этот подход не дает преимуществ перед классовым.
Вовсе не тонну. Посмотрите исходный код. Там не так много срок. И не в 100 раз быстрее. Вы утрируете. А если учесть сколько времени уйдет на отладку непривычного кода, то может выясниться что такой подход дороже.
Я понял вашу корректировку. И признался честно, что ошибся в терминологии. Люди почитают комментарии и увидят. Однако и вы поймите, что я знаю про прототипирование и умею наследовать стандартными средсвтами JS. Однако считаю (и не я один), что удобнее иметь обертку для придания коду внешнего вида близкого к тому, какой бы был при наличии классов.
Еще раз (и последний :)) - идеология другая, а не только терминология. Возьмите, к примеру, не вышедший ES4 (JS2):
Но и это не основное (на уровне реализации движка, вероятно, сделаны оптимизации, и это, все-таки один метод).
Полная динамика объектов, возможность динамически менять родителей, расширять объекты, добавляя им свое поведение (методы) - это разница в идеологиях между статической классовой парадигмой и динамической классовой или прототипной.
Ну так и надо тогда говорить, что Вы не о JavaScript’e пишите. Я лишь уточнил, чтобы те, кто будут читать статью, не думали о JS неграмотно.
Динамика, композиция, агрегация, примеси (расширение своим поведением и состоянием), смена предков и т.д.
И что Вы имеете в виду под преимуществом? Статическая классовая модель, к тому же и быстрее (и намного) динамической модели с mutable-объектами. И, мне кажется, Вы не верный ход придаете дискуссии - я вполне объективно отношусь к любой из парадигм, не пропагандируя особые.
Еще раз - я вполне адекватно и объективно отношусь к новым абстракциям. Естественно, я утрирую. И 100 раз, это еше мало. 100000 - вполне нормально. Если у Вас сенсорно-прикладное использование технологии, то, естественно, заморачиваться не стоит. В любом случае, новые абстракции разрабатываются для улучшения разработки, и приходится жертвовать ресурсами.
Классов-не классов - не имеет значение. Я холиворов на эти темы не устраиваю (поскольку осознаю, что не я создатель этих парадигм), я лишь изучаю эти подходы. Дело в code reuse - и выдвижение сущности “класс” вполне обосновано идеологически. И обертки - это тоже - всего лишь code reuse + синтаксический сахар. Конечно, обертки, улучшают разработку, но, - жертвуя ресурсами (это общий закон усиления абстракций - любых абстракций). При этом (еще раз повторю) - если брать динамическую классовую организацию (например, Python), то там схожесть очень большая с “конструктор + прототип”. Если же брать Java или C++ - естественно, отличия есть.
Я понял и с первого раза вашу мысль. Но как я уже говорил, я знаю этот подход. Я просто не знал, что это тоже ООП. Тоесть моя ошибка только в терминологии. И я это признал.
Ну а на практике вы где то это применяете? Были у вас реальные задачи, которые на классах не решить? У меня таких задач не было. И поскольку задачи были довольно сложные, то могу предположить, что прототипный подход не дает каких-то особых преимуществ на практике. Ну изменили мы в рантайме предка у объекта, ну и что? Научная ценность? Игра разума?
Жертвуя ресурсами, да. Но пока у вас нет на руках тестов, то говорить о том насколько велика эта жертва это неграмотный подход.
Во избежание непонимания я попытаюсь резюмировать что я хотел сказать.
- Когда я писал статью я знал о протитипном походе. Знал синтаксис и принцип. Возможно не досканально. Но знал. И не знал я лишь, что это тоже часть ООП (только другая ветка).
- Преимущества прототипного подхода мне не удалось применить на практике.
- В большинстве языков, с которым пользователь сталкивается до того как познакомится с JS используется ООП с классами.
Так как привычнее для обычного программиста похоже классы, а не прототипы, и так практических задач которые решаются только с прототипами не вы не я не знаете, то считаю обертки типа classy.js и prototype.js вполне удобными. Благодаря использованию таких оберток в коде будет меньше ошибок, а значит и дешевле разработка.
Каждый конечно же выбирает сам. Мое мнение, что использовать обертки дешевле.
Еще раз - “классы - не классы” - сути не меняет. Разницу в идеологиях можно вести о “статике-динамике”, поскольку динамическая классовая модель схожа с “конструктор + прототип”.
По поводу реальных задач - к примеру, ActiveRecord их Ruby on Rails - там, благодаря полной динамике, появляются методы, позволяющие еще больше усилить абстракцию (если в таблице есть поле name, то, помимо метода find, появится (динамически, в рантайме) еще и find_by_name и т.д.)
Я вообще не пойму - что Вы спорите? “Классы”, “прототипы” и т.д. - всего лишь новые уровни абстракции. К примеру, в системном программировании, их нет. Вам там скажут - “покажите реальные задачи, которые без процедурного программирования не решить? Какие еще прототипы? Какие еще классы?”. Я всего лишь поправил Вас в неверных утверждениях относительно JavaScript (поскольку Вы взялись вести разговор о JavaScript).
Ну, это уже другой разговор. Кто-то же придумал для Вас “классы” и прочие абстракции. “Там” наиболее ключевые фигуры - это “создатели” и “потребители продуктов создателей” (причем, “потребители” на новых уровнях абстракций могут становится “создателями”). Поэтому, не стоит саркастировать о “научных ценностях” - кто создает эти идеологии (чтобы потом потребители холиворили на всяких форумах, при этом, не являясь создателям ни той, ни другой идеологии) - далеко не являются лишь потребителями с целью дешевой разработки. Но при этом, ими так же движет облегчение, автоматизация, создание новых оберток и т.д. (и как следствие - минимум ручного ввода кода).
Говорите за себя, пожалуйста. Я всего лишь показываю разницы и (повторяю) - в большей степени - “динамика-статика” (поскольку “прототип-класс” при динамических классовых моделях не значительны).
Вы путаете. Я ж, вероятно, не учел, что предмет Вам не столь и интересен. Вам интересна дешевая разработка. И это верно, это правильно! И именно к этому движутся идеи создателей (тех, кто придумывает за Вас эти идеологии), но вот цели, возможно, разные.
Мое резюме:
- хрен с ней с терминологией, в которой Вы ошиблись, это не страшно.
- разница между “прототип-класс” при динамической организации - не значительная
- разница между “статикой-динамикой” - огромная.
- вести “дешевую” (человекоресурсам) и “дорогую” (по системным ресурсм) - это линейных ход усиления абстракций и этот ход является верным. При этом, эта “дешевая” разработка может быть осуществлена с применением разных технологий.
Говоря, об усилениях абстракций, я имею в виду более глобальный уровень, нежели JS -> jQuery + Classy.js. И потеря в ресурсах (для уменьшения сил человека) в любом случае имеет место быть (никакие тесты тут не важны).
https://habrahabr.ru/blogs/webdev/45552/#comment_1152704
https://anton.shevchuk.name/javascript/html-css-javascript-standarts/#comment-32944
P.S> можно объединить это сообщение с предыдущим, чтобы не плодить много комментов.
Судя по тому что вы так и не сказали что вам пригодились прототипы в задачах, которые вы решаете на JS, я прав. Я уже не первый раз у вас интересуюсь чем вас спасают эти прототипы. И так ответа и не получил. Переносить разговор в область других языков и технологий я бы не хотел. Статья о JS.
Я просто не занимаюсь программированием ради программирования. Я решаю поставленные задачи с имеющимися инструментами. Поэтому время дорого.
То что путь написания оберток ведет к замедлению я не спорю. Но пока у меня нет тестов или я не вижу визуально, что с оберткой ощутимо тормозит, то не вижу причин писать на голом JS.
С вашим резюме в общем-то согласен. Что вы пытаетесь доказать (рассказать) ?
Как же не получили? ActiveRecord? Плагины к любимому jQuery пишите? А как это новый метод, вдруг, доступен всем объектам? (посмотрите выше - я приводил эти примеры).
И это Вы меня не слышите, я основной мыслью выделяю “статику-динамику” (поскольку прототипы схожи с динамической классовой моделью). Вы это слышите? Вам интересно это? Вам нужно это знать? Если нет - я позволю себе воздержусь от объяснений.
Да я в курсе и понимаю, я это тоже отметил, даже пару ссылок привел. И это правильно, если Вы занимаетесь прикладным программированием и саму технологию знать особо не нужно.
Я не пытаюсь. Я уточняю и поправляю, поскольку статья переполнена неверными утверждениями. И именно утверждениями. Ладно бы Вы предполагали, но Вы же утверждаете. Вы объяснили, что не знали, что “это” тоже подпадает под терминологию “ООП”. Это замечательно, в этом ничего страшного не было, так я и не винил Вас за это! Я всего лишь сделал поправку, чтобы те, кто будет читать статью, не поняли неправильно.
А дальше Вы пытаетесь задать неверный ход дискуссии, полагая, что я возвышаю динамический подход (и в JS он прототипный) против статического классового. Это вовсе не так, я, опять же, лишь привожу примеры и показываю разницу (поскольку Вы интересовались. Если это не интересно и знать не нужно - я позволю себе воздержаться). В разных языках я использую наиболее оптимальные подходы.
Который раз говорю, что основное отличие в “статике-динамике”, уточняю, что прототипирование схоже с динамическими классами, а Вы мне снова - “я так и не увидел, чем хороши прототипы”. Еще раз - “динамика”. ActiveRecord, динамические плагины, методы и т.д. Я привел пример? Я ответил на вопрос?
А если я спрошу, какие еще “классы” появились у Вас в наличии при использовании обертки? Классы из Java? Из C++? Или может из Python? Так в Python - и так - такая же модель, просто там это названо “класс”. Поэтому, если хотите говорить “я не вижу преимуществ”, то говорите “я не вижу преимуществ динамической модели от статической”, а не “прототипы, прототипы”.
То, что вы функцию обернули в другую функцию и назвали ее класс - это не значит, что появились какие-то “Java/C++ классы”, а динамические - и так есть (в кавычках, правда) - это “конструктор + прототип” (та же картина в Питоне, только там это названо “класс”).
А никто и не заставляет писать на голом JS. Я наоборот - за усиление абстракций (и уже описывал, что это - есть прогресс). При этом сам JS уже очень большая абстракция, относительно ассемблера, например.
Я передал Вам достаточно новой информации? Ведь следующая же статья уже не будет содержать утверждения, которые были в этой, да? Ну вот и замечательно.
Не пишу. Я на jQuery недавно. Большую часть сознательной жизни на prototype.js был. Но и там у элементов появляются новые методы. Вот это уже пример в тему.
Конечно интересно.
Тогда прошу прощения. Я погорячился наверное. Не так интерпретировал ваши поправки.
Не могу сказать, что достаточно, но не мало. И за это я вам безмерно благодарен. Побольше бы в блоге таких читателей как вы. Понимаю правда, что и статьи тогда надо писать соответствующие.
Надеюсь что не будет. Спасибо вам за поправки. Буду рад, если вы прочитаете другие, статьи про JS когда они появятся. Как бы мне еще эту статью сделать не такой ужасной….?