Pull to refresh

Кунг-фу: стиль JavaScript

Website development
Эта статья начиналась как комментарий к другой статье на habrahabr. После написания первого листа, я понял, комментарий слишком обширный получился :). Я решил написать, потому что хочу заострить внимание на моментах, которые, на мой взгляд, были упущены. Ограничение этой статьи — моя цель изложить всё максимально доступно, не ищите здесь математической точности в определении терминов, и всё же я прилагаю ссылки где математики найдут высококлассные понятные только им определения :)

Наверно каждую статью по JS принято начинать со слов о его недооцененности :) Это правда :) Когда я пару лет назад говорил о том что JS мой любимый язык на меня смотрели, как на школьника-переростка, который только что написал свою первую страницу на HTML, а те кто меня знал, как на гроссмейстера, который сказал что он только и знает как фигуры ходят :). Таких людей не стало намного меньше, увы :(

Итак, момент первый — ничего не сказано о том чем Javascript отличается принципиально от Java или С++. Отличие в подходе (парадигме) программирования взятой за основу при создании языка. Я не оцениваю «что лучше?», ни в коме случае, кому что удобнее :) или нравиться :)


Отличие традиционной класс-объектной от прототипной парадигмы


Большинство людей, которые смотрят на JS свысока говорят о нем, как не о ООП языке :). Начнем с того, что это разные вещи и их вообще нельзя сравнивать. Конечно можно программировать на JS пользуясь навыками полученными при программировании на C++ или Java, но это так не эффективно. JS работает иначе, в нём нет классов, в нём есть объекты (не падайте в обморок, дочитайте статью). Это не означает, что на JS невозможно сделать абстракции, инкапсуляции, наследование или полиморфизма. Главные отличия в реализации инкапсуляции, наследования, и работе конструктора. В JS, как и почти во всех языках прототипного типа, нужно использовать термин инкапсуляции, есть замыкание.

Замыкание


Замечу, что на хабре уже была статья о замыканиях. Попробую объяснить «на пальцах» и добавлю пример.

Замыканиями в JS называют область видимости внутри которой переменная обретает смысл :). Это может быть определение объекта или функции. Переменная может быть как примитивной, так и объектом или функцией. Уф… теперь, любимое, пример:
  1. function bicubic(count)
  2. {
  3. var values = [];
  4. for (var i = 0; i < count; i++)
  5. {
  6. values[i] = (
  7. function(n)
  8. {
  9. return function()
  10. {
  11. return Math.pow(n, 3);
  12. }
  13. }
  14. )(i);
  15. }
  16. return values;
  17. }
  18. var vals = bicubic(10);
  19. alert( vals[3]() );
* This source code was highlighted with Source Code Highlighter.
Если бы мне показали эти строки и попросили бы сказать «что это за хрень такая» я бы не одну минуту потратил, чтоб разобраться :) Именно это и придает столько силы сторонником JS в утверждении, что языки основанные на классах приводят при написании кода к излишней сфокусированности программистов на иерархичности и связях классов друг с другом, что по моему правда.

Давайте по порядку :)
  1. Создается функция bicubic которая выдает массив;
  2. Фишка в том, что является членами массива — это функции без параметра: Math.pow(n, 3). В данном случае n, благодаря чуду замыкания становиться значением i в момент прохода цикла, а не после него!
  3. Волшебство в скобках (...)(i) — это вызов функции с параметром i, а в скобках определение этой функции.

т.к после прохода циклом i = count, то без этих скобок, мы получим совсем другой ответ — постоянный 10'000, а с ними 27.

Кстати, этот приём называется разрывом замыкания.

Именно поэтому в jQuery рекомендуют писать плагины к этому фреймверку внутри конструкции (… ваш плагин...)();, это позволяет:
  1. Писать безопасный код
  2. Не перегружать браузер глобальными переменными

Экземпляр, конструктор, объект и прототип


Вот я недавно написал, что в JS нет классов, а есть объекты. Я имел в виду следующее:

В класс-ориентированных языках новый экземпляр создается через вызов конструктора класса (возможно, с набором параметров). Получившийся экземпляр имеет структуру и поведение, жёстко заданные его классом. Вот строчки из Википедии:
В прототип-ориентированных системах (например JS) предоставляется два метода создания нового объекта: клонирование существующего объекта, либо создание объекта «с нуля». Для создания объекта с нуля программисту предоставляются синтаксические средства добавления свойств и методов в объект. В дальнейшем, с получившегося объекта может быть получена полная копия, клон. В процессе клонирования копия наследует все характеристики своего прототипа, но с этого момента она становится самостоятельной и может быть изменена. В некоторых реализациях копии хранят ссылки на объекты-прототипы, делегируя им часть своей функциональности; при этом изменение прототипа может затронуть все его копии. В других реализациях новые объекты полностью независимы от своих прототипов.
Демонстрация:
  1. function win() {
  2. this.open = function()
  3. {
  4. return "open 1";
  5. }
  6. }
  7. win.open = function() {
  8. return "open 2";
  9. }
  10. alert( win.open() ); // 1. open 2
  11. win.prototype = {
  12. save: function()
  13. {
  14. return "open 3";
  15. }
  16. }
  17. alert( win.save() ); // 2. error - нет save
  18. win.prototype.open = function()
  19. {
  20. return "open 4";
  21. }
  22. alert( win.open() ); // 3. open 2
  23. var vista = new win();
  24. alert( vista.open() ); // 4. open 1
  25. alert( vista.save() ); // 5. open 3
  26. vista.open = (
  27. function()
  28. {
  29. return "open 5";
  30. }
  31. )();
  32. alert( vista.open ); // 6. open 5
  33. alert( win.open() ); // 7. open 2
* This source code was highlighted with Source Code Highlighter.

«open 4» мы не получим, т.к. метод open у нас определен в самом объекте, что не касается save.

Внимательный читатель меня спросит — а где же в этой дьявольской смеси конструктор? Да, здесь его не было. Конструктором является сам объект, запускается при создании клона, с помощью оператора new или инициализации самого объекта. Демонстрация:
  1. function win()
  2. {
  3. var str = "open 1";
  4. this.open = function()
  5. {
  6. return str;
  7. }
  8. }
  9. alert( (new win()).open() ); // open 1
* This source code was highlighted with Source Code Highlighter.
Создание объекта «с нуля» в JS, «это вещь»! Приведу пример, который помогает экономить мускульные усилия пальцев:
  1. function plugin(options)
  2. {
  3. options = options || {};
  4. options.list = options.list || [];
  5. // далее можно смело работать
  6. // options, проверять его свойства, например,
  7. // а с list как с массивом
  8. }
* This source code was highlighted with Source Code Highlighter.


Прототип, «наследование» в javascript



очень сложно найти в тёмной комнате чёрную кошку, особенно если там её нет :)
© народная мудрость

В JS нет наследования и тем более множественного. У каждого объекта есть прототип, объект который можно сделать общим для нескольких объектов или их экземпляров, а потом менять :)


Итог


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

Остальные, надеюсь узнали что-нибудь полезное :)

С уважением, Артур Дудник
самая ценная человеческая черта — стремление к самосовершенствованию ©

Tags:javascriptobjectprototypejs
Hubs: Website development
Total votes 104: ↑96 and ↓8+88
Views1.9K

Popular right now