Этот странный язык JavaScript

9 июнь, 2006 - 14:48Виктор Вейтман

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

Если начинающий программист захочет выяснить, является ли JavaScript объектно-ориентированным языком, сделать это будет совсем непросто. Из одного руководства он узнает, что JavaScript – несомненно объектно-ориентированный, а из другого – что процедурный. Автор одного из популярных учебников по JavaScript Стефан Кох (Stephan Koch) вообще деликатно обходит этот вопрос. Но Дуглас Кроуфорд (Douglas Crawford), известный JavaScript-гуру, убеждает, что JavaScript-сценарий имеет дело с объектами и только с ними. Наверное, истина где-то посередине, и мы не погрешим против нее, если согласимся, что данный язык все-таки процедурный, хотя и позволяет выполнять определенный набор действий с объектами. При этом средства поддержки ООП в JavaScript большинству программистов покажутся, мягко говоря, непривычными.

Начнем с внутреннего представления. Возможно, не все догадываются, что в этом языке объект – не что иное, как ассоциативный массив. В него помещаются значения свойств или коды функций, а имена служат ключами доступа к ним. Другими словами, выражения myObject.myProperty и myObject[«myProperty»] вернут абсолютно одинаковый результат.

Объект создается путем вызова конструктора, здесь трудно заметить различия между JavaScript и обычными объектно-ориентированными языками, но если мы захотим, проанализировав код, выяснить подробности этого процесса, то обнаружим, что описание объекта... отсутствует. Другими словами – в языке нет классов. Соответственно, конструктор поместит в состав объекта лишь те свойства и функции, которые определены в нем самом. Однако и на этом чудеса не заканчиваются. При желании программист может в любой момент добавить к объекту новое свойство или функцию, и точно так же он может в любой момент прочитать или модифицировать значение свойства, используя для этого «объектную нотацию» или напрямую обратившись к элементу массива. Функции также доступны в любом месте программы. Более того, существует механизм заимствования (borrowing), позволяющий одному объекту выполнять в своем контексте код, принадлежащий другому. Используя его без надлежащей подготовки, можно получить совершенно непредсказуемые результаты.

Казалось бы, в подобной ситуации разговор об инкапсуляции попросту неуместен, однако отдадим должное таланту Дугласа Кроуфорда. Он заметил, что если в конструкторе объявить переменную с помощью ключевого слова var, то доступ к ней станет возможным только для функций, определенных в самом конструкторе. Решение, конечно же, блестящее, но можно ли требовать от «среднестатистического» разработчика знания подобных трюков?

Не лучше обстоит дело и с наследованием. Для его реализации применяется механизм прототипов. Он определяет набор свойств и функций, которые автоматически получает вновь создаваемый объект. И тут нас ждет новый сюрприз. Оказывается, два объекта, созданные с помощью одного и того же конструктора, могут иметь различную структуру! Дело в том, что никто не мешает в любой момент изменить прототип. Может ли наследование оказаться полезным в подобной ситуации? Каждый программист волен ответить на этот вопрос по-своему. Многие даже считают, что наследование на основе прототипов открывает новые возможности по сравнению с классическим подходом. Однако главное в данном случае – не личные предпочтения, а удобство организации совместной работы и возможность повторного использования кода.

О перегрузке не приходится даже говорить. Имя метода является ключом для обращения к его коду, а два элемента ассоциативного массива не могут иметь один и тот же ключ. Но определив метод с набором параметров, отличных от уже существующих, мы не получим сообщения об ошибке – новый метод попросту заменит старый. Не исключено, что кто-то из энтузиастов JavaScript предложит оригинальное решение и этой проблемы, но оно опять же станет рецептом для «интеллектуальной элиты», а не для рядового разработчика.

Конечно, сказанное выше совсем не означает, что на JavaScript нельзя написать объектно-ориентированную программу. Сделать это можно даже на ассемблере, только никто всерьез не рассматривает его в качестве базового средства для создания больших проектов. Куда проще выдержать хороший стиль программирования, когда сам язык «подталкивает» вас к этому.

Так что же выходит: больше десяти лет JavaScript всех устраивал, а теперь вдруг перестал? Как ни странно, дело обстоит именно так. Еще недавно большинство сценариев, включаемых в состав Web-документа, умещались в несколько десятков строк и можно было с улыбкой рассуждать о «милых чудачествах» JavaScript. Однако сегодня, с популяризацией Ajax, размер сценариев, выполняющихся на стороне клиента, многократно возрастает, а это автоматически приводит к существенному увеличению числа участников проекта. Последствия нетрудно предугадать – процедурные языки уже давно продемонстрировали предел своих возможностей. Более или менее сложный код, написанный на них, быстро становится неуправляемым. Искусственно созданные объекты тоже вряд ли оправдают себя – программисты средней квалификации могут не учесть всех нюансов.

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