Взаимодействие с платформой

ClojureScript компилируется в JavaScript и выполняется в браузере. Это означает, что либо стандартная библиотека языка должна иметь все средства для взаимодействия с браузером, либо язык должен предоставлять удобные средства для взаимодействия со средой исполнения JavaScript. Язык Clojure в целом следует второму пути и ClojureScript в частности позволяет вашему коду общаться с браузером посредством уже имеющегося JavaScript API.

Теперь, когда у вас уже есть настроенный проект, можно начать применять ClJS на практике. Реализуем приложение, которое бы при загрузке страницы модифицировало содержимое оной. В index.html уже имеется тег <div id="root">, его содержимое мы и будем менять. В файле src/main/com/frontend/app.cljs (в ближайшее время весь код будет относиться к этому файлу) опишите функцию следующего вида:

(defn update []
  (-> (.querySelector js/document "#root")
      .-innerHTML
      (set! "<h1>Hello World!</h1>")))

Здесь используются сразу три вида взаимодействия с JavaScript objects:

  1. (.querySelector js/document "#root") — это вызов метода querySelector применительно к объекту document со строкой "#root" в качестве аргумента. Такой вызов в JavaScript выглядел бы как строчка document.querySelector("#root").
  2. (.-innerHTML node) — обращение к атрибуту объекта, эквивалентное выражению node.innerHTML в JS.
  3. (set! value attribute) — присвоение нового значения атрибуту объекта. В JS это выглядело бы как object.attribute = value.

В примере выше обращение к полю не обёрнуто в скобки, поскольку макрос -> позволяет опускать оные, если на текущем шаге конвейера нужно применить функцию к одному лишь передаваемому по конвееру аргументу. С тем же успехом можно было написать (-> .. (.-innerHTML) ..), но макрос -> позволяет слегка сэкономить на скобках. И возможно такое упрощение потому, что обращение к атрибуту — тоже функция. Сам же макрос -> ничего не знает об объектах и атрибутах!

Теперь, когда у вас есть функция update, её можно вызвать из "точки входа в программу" — из функции init:

(defn init []
  (update))

Попробуйте открыть страницу при запущенном dev server, вы должны увидеть вставленный в тело страницы тег h1 с текстом приветствия.

Вот ещё несколько примеров использования JS interoperability:

(defn log-some-date []
  (let [date (js/Date. 1999 12 31)]  ;; создание объекта
    (.log js/console (.toISOString date))))  ;; вызовы методов

Здесь (js/Date. ...), это аналог создания нового объекта типа Date. В JavaScript вы бы написали new Date(...). Заметьте: точка в конце имени типа означает, что вы вызываете конструктор данного типа, без точки вы бы получили доступ к самому классу js/Date, который не может быть вызван как функция сам по себе.