image Большинство языков программирования выросли из древней парадигмы, порожденной еще во времена Фортрана. Гуру JavaScript Дуглас Крокфорд выкорчевывает эти засохшие корни, позволяя нам задуматься над будущим программирования, перейдя на новый уровень понимания требований к Следующему Языку (The Next Language).

Автор начинает с основ: имен, чисел, логических значений, символов и другой базовой информации. Вы узнаете не только о проблемах и трудностях работы с типами в JavaScript, но и о том, как их можно обойти. Затем вы приступите к знакомству со структурами данных и функции, чтобы разобраться с механизмами, лежащими в их основе, и научитесь использовать функции высшего порядка и объектно-ориентированный стиль программирования без классов.

Отрывок
Как работает код без классов


И думаешь, что ты умен вне всяких классов и свободен.
Джон Леннон (John Lennon)

Одной из ключевых идей в разработке объектно-ориентированного программирования была модель обмена данными между частями программы. Имя метода и его аргументы нужно представлять в виде сообщений. Вызов метода посылает объекту сообщение. Каждый объект характеризуется собственным поведением, которое проявляется при получении конкретных сообщений. Отправитель полагает, что получатель знает, что ему делать с сообщением.

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

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

Кроме того, мы наблюдаем повышенное внимание к свойствам, а не к объектам. Особое внимание уделяется методам получения (get-методам) и присваивания (set-методам) значений каждому отдельно взятому свойству, а в еще менее удачных проектах свойства являются открытыми и могут быть изменены без ведома объекта. Вполне возможно ввести в обиход более удачный проект, где свойства скрыты, а методы обрабатывают транзакции, не занимаясь только лишь изменением свойств. Но такой подход применяется нечасто.

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

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

Типы не виноваты в появлении труднообнаруживаемых и дорогостоящих ошибок. Их вины нет и в возникновении проблем, вызываемых такими ошибками и требующих каких-то уловок. Типы могут подтолкнуть нас к использованию малопонятных, запутанных и сомнительных методов программирования.

Типы похожи на диету для похудания. Диету не обвиняют в возвращении и увеличении веса. Ее также не считают причиной страданий или вызванных ею проблем со здоровьем. Диеты вселяют надежду, что вес придет в здоровую норму и мы продолжим есть нездоровую пищу.

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

Конструктор


В главе 13 мы работали с фабриками — функциями, возвращающими функции. Что-то похожее теперь можем сделать с конструкторами — функциями, возвращающими объекты, которые содержат функции.

Начнем с создания counter_constructor, похожего на генератор counter. У него два метода, up и down:

function counter_constructor() {
      let counter = 0;

      function up() {
            counter += 1;
            return counter;
      }

      function down() {
            counter -= 1;
            return counter;
      }

      return Object.freeze({
            up,
            down
      });
}

Возвращаемый объект заморожен. Он не может быть испорчен или поврежден. У объекта есть состояние. Переменная counter — закрытое свойство объекта. Обратиться к ней можно только через методы. И нам не нужно использовать this.

Это весьма важное обстоятельство. Интерфейсом объекта являются исключительно методы. У него очень крепкая оболочка. Мы получаем наилучшую инкапсуляцию. Прямого доступа к данным нет. Это весьма качественная модульная конструкция.

Конструктор — это функция, возвращающая объект. Параметры и переменные конструктора становятся закрытыми свойствами объекта. В нем нет открытых свойств, состоящих из данных. Внутренние функции становятся методами объекта. Они превращают свойства в закрытые. Методы, попадающие в замороженный объект, являются открытыми.

В методах должны реализовываться транзакции. Предположим, к примеру, что у нас есть объект person. Может потребоваться изменить адрес того лица, чьи данные в нем хранятся. Для этого не нужен отдельный набор функций для изменения каждого отдельного элемента адреса. Нужен один метод, получающий объектный литерал, способный дать описание всех частей адреса, нуждающихся в изменении.

Одна из блестящих идей в JavaScript — объектный литерал. Это приятный и выразительный синтаксис для кластеризации информации. Создавая методы, потребляющие и создающие объекты данных, можно сократить количество методов, повышая тем самым целостность объекта.

Получается, у нас есть два типа объектов.

  • Жесткие объекты содержат только методы. Эти объекты защищают целостность данных, содержащихся в замыкании. Они обеспечивают нас полиморфизмом и инкапсуляцией.
  • Мягкие объекты данных содержат только данные. Поведение у них отсутствует. Это просто удобная коллекция, с которой могут работать функции.

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

Если жесткий объект должен быть преобразован в строку, нужно включить метод toJSON. Иначе JSON.stringify увидит его как пустой объект, проигнорировав методы и скрытые данные (см. главу 22).

Параметры конструктора


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

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

Это дало бы множество преимуществ.

  • Ключевые строки придают коду задокументированный вид. Код легче читается, поскольку он сам сообщает вам, что представляет собой каждый аргумент вызывающей стороны.
  • Аргументы могут располагаться в любом порядке.
  • В будущем можно добавлять новые аргументы, не повреждая существующий код.
  • Неактуальные параметры можно игнорировать.

Чаще всего параметр используют для инициализации закрытого свойства. Это делается следующим образом:

function my_little_constructor(spec) {
      let {
           name, mana_cost, colors, type, supertypes, types, subtypes, text,
           flavor, power, toughness, loyalty, timeshifted, hand, life
      } = spec;

Этот код создает и инициализирует 15 закрытых переменных, используя свойства с такими же именами из spec. Если в spec нет соответствующего свойства, происходит инициализация новой переменной, которой присваивается значение undefined. Это позволяет заполнять все пропущенное значениями по умолчанию.

Композиция


Яркая выразительность и эффективность JavaScript позволяют создавать программы в классической парадигме, хотя этот язык и не относится к классическим. JavaScript позволяет также вносить улучшения. Мы можем работать с функциональной композицией. Итак, вместо добавления чего-то в качестве исключения можно получить понемногу того и этого. Конструктор имеет следующий общий вид:

function my_little_constructor(spec) {
      let {компонент} = spec;
      const повторно_используемый = other_constructor(spec);
      const метод = function () {
            // могут применяться spec, компонент, повторно_используемый, метод
      };
      return Object.freeze({
            метод,
            повторно_используемый.полезный
      });
}

Ваш конструктор способен вызвать столько других конструкторов, сколько нужно для получения доступа к управлению состоянием и обеспечиваемому ими поведению. Ему даже можно передать точно такой же объект spec. Документируя spec-параметры, мы перечисляем свойства, нужные my_little_constructor, и свойства, необходимые другим конструкторам.

Иногда можно просто добавить полученные методы к замороженному объекту. В иных случаях у нас есть новые методы, вызывающие полученные методы. Тем самым достигается повторное использование кода, похожее на наследование, но без сильного сцепления. Вызов функции является исходной схемой повторного применения кода, и ничего лучше еще не придумано.

Размер


При таком подходе к конструированию объекта задействуется больше памяти, чем при использовании прототипов, поскольку каждый жесткий объект содержит все методы объекта, а прототипный объект содержит ссылку на прототип, содержащий методы. Существенна ли разница в потреблении памяти? Соизмеряя разницу с последними достижениями в повышении объема памяти, можно сказать: нет. Мы привыкли считать память в килобайтах. А теперь считаем ее в гигабайтах. На этом фоне разница совершенно не чувствуется.

Разницу можно сократить, улучшив модульность. Акцент на транзакциях, а не на свойствах позволяет уменьшить количество методов, а заодно улучшить связанность.

Классическая модель характеризуется однообразием. Каждый объект должен быть экземпляром класса. JavaScript снимает эти ограничения. Не все объекты нуждаются в соблюдении столь жестких правил.

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

» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок

Для Хаброжителей скидка 25% по купону — JavaScript

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.

Комментарии (4)


  1. JustSkiv
    11.06.2019 16:16

    Хотелось бы увидеть отрывок книги с главой «Сначала прочитайте меня!». К примеру, возможно, автор там указывает целевую аудиторию книги — для профессионалов, новичков и т.п. Конечно, по имеющемуся отрывку можно сделать предположения, но всё же.


  1. ph_piter Автор
    11.06.2019 18:11

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

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

    Здесь не рассматриваются механизмы обработки кода JavaScript или виртуальные машины. Книга — о самом языке и о том, что должен знать каждый программист. В ней я попробую сделать радикальную переоценку JavaScript, того, как он работает, как его можно усовершенствовать и как лучше использовать. Речь идет о том, как думать о JavaScript и как думать в JavaScript. Я планирую притвориться, что текущая версия языка — единственная, и не собираюсь тратить ваше время на демонстрацию того, как все работает в ES1, ES3 или ES5. Это не имеет никакого значения. Основное внимание будет уделено тому, как JavaScript работает для нас именно сейчас.

    Эта книга не исчерпывающее руководство. В ней без какого-либо специального упоминания будут проигнорированы довольно большие и сложные части языка. Если не упомянута ваша самая любимая функция, то это, скорее всего, потому, что она не представляет ни малейшей ценности. Синтаксис также не будет удостоен особого внимания. Предполагается, что вы уже знаете, как написать инструкцию if.

    Важная цель при разработке языка программирования — сделать так, чтобы он был понятен, логичен и хорошо сформулирован, не приводил к возникновению странных тупиковых ситуаций. Но JavaScript даже близко не подвели к достижению этой цели. С каждым выпуском языка его странности растут как снежный ком, неизменно усугубляя ситуацию. В нем появляется множество тупиковых и критических проблем. В книге рассматриваются только некоторые из этих странностей, просто чтобы показать наличие подобных безобразий. Держитесь подальше от всего, что приводит к таким тупиковым и крайним ситуациям. Не углубляйтесь в этот мрак. Оставайтесь в той части языка, где все просто и понятно. Там есть все, что вам нужно для написания хороших программ».


    1. JustSkiv
      14.06.2019 22:20

      Шикарно, именно это я и искал. Все книги по JS, что я встречал, написаны совсем для новичков и учат скорее основам программирования.


  1. flancer
    12.06.2019 09:17

    Получается, у нас есть два типа объектов.
    • Жесткие объекты содержат только методы. ...
    • Мягкие объекты данных содержат только данные. ...

    интересная мысль, отсылающая нас к Гарвардской архитектуре ЭВМ.