Сумісність з батьківською платформою

ClojureScript — це гостьова мова так само, як і її сестра Clojure. Це означає, що обидві мови орієнтовані на інтеграцію з батьківською платформою (JavaScript для ClojureScript та JVM для Clojure).

Типи

ClojureScript прагне використовувати усі існуючі типи з JavaScript, хоча це може здатися несподіваним для мови, що не має нічого спільного з JavaScript. Ось неповний список особливостей, що наслідуються з батьківської платформи та використовуються в ClojureScript:

  • Рядки в ClojureScript — це звичайні рядки з JavaScript
  • Числа у ClojureScript — ті самі, що і в JavaScript
  • Значення nil в ClojureScript перетворюється на null у JavaScript
  • Регулярні вирази в ClojureScript — екземпляри класу RegExp з JavaScript
  • ClojureScript не інтерпретована мова; вона завжди компілюється у JavaScript
  • В ClojureScript дуже просто використовувати API батьківської платформи, при цьому семантика таких API не змінюється
  • Усі структури даних в ClojureScript компілюються в об'єкти в JavaScript, що імплементують ці структури даних

Окрім цього, ClojureScript будує власні абстракції та структури даних, яких немає у батьківській платформі — вектори, мапи, множини та інші, що були розглянуті у попередніх частинах.

Взаємодія з типами даних батьківської платформи

У ClojureScript є невеликий набір спеціальних форм для роботи з типами даних батьківської платформи, наприклад: виклик методу об'єкту, створення екземплярів класів та доступ до властивостей об'єктів.

Доступ до платформи

Доступ до середовища батьківської платформи у ClojureScript здійснюється через простір імен js/. Ось приклад використання функції parseInt з JavaScript:

(js/parseInt "222")
;; => 222
Створення екземплярів класу

Створити екземпляр класу у ClojureScript можна двома способами:

За допомогою спеціальної форми new

(new js/RegExp "^foo$")

Або спеціальної форми .

(js/RegExp. "^foo$")

Рекомендованою формою створення екземплярів є друга — саме їй надає перевагу спільнота ClojureScript, хоча нам невідомі суттєві відмінності між цими двома формами.

Виклик методів об'єкту

Виклик методів об'єкту в ClojureScript дещо відрізняється від JavaScript, де ми пишемо так: obj.method(). В ClojureScript спочатку записується назва методу, а потім назва об'єкту, а не навпаки. Також, на початку назви методу додається спеціальна форма .

Розглянемо як приклад виклик методу .test() регулярного виразу:

(def re (js/RegExp "^Clojure"))

(.test re "ClojureScript")
;; => true

Методи екземплярів обʼєктів JavaScript також можна викликати. Перший приклад використовує вже знайому нам форму запису, а другий — демонструє скорочену форму:

(.sqrt js/Math 2)
;; => 1.4142135623730951
(js/Math.sqrt 2)
;; => 1.4142135623730951
Доступ до властивостей об'єкту

Синтаксис доступу до властивостей об'єкту дуже схожий на виклик методу, але замість . використовується .-. Розглянемо приклад:

(.-multiline re)
;; => false
(.-PI js/Math)
;; => 3.141592653589793
Скорочена форма доступу до властивостей об'єкту

Символи з префіксом js/ можуть містити крапки для позначення доступу до вкладених властивостей. Обидва наступні вирази викликають одну функцію:

(.log js/console "Hello World")

(js/console.log "Hello World")

А ці вирази вирази отримують доступ до однієї властивості обʼєкту:

(.-PI js/Math)
;; => 3.141592653589793

js/Math.PI
;; => 3.141592653589793
Об'єкти JavaScript

ClojureScript пропонує кілька способів створення об'єктів; кожен з них має свою сферу застосування. Функція js-obj приймає будь-яку кількість пар «ключ-значення» і повертає об'єкт JavaScript:

(js-obj "country" "FR")
;; => #js {:country "FR"}

Це може стати вам у нагоді, коли треба працювати зі сторонніми бібліотеками JavaScript, що приймають звичайні об'єкти. У прикладі вище ви могли побачити, як виглядає об'єкт в ClojureScript. Насправді, це ще одна форма запису.

#js — це макрос читача, що додається до мап та векторів, які будуть трансформовані у об'єкти та масиви JavaScript:

(def myobj #js {:country "FR"})

Те саме у JavaScript:

var myobj = {country: "FR"};

У попередньому розділі ми згадували про те, що доступ до властивостей обʼєктів можливий за допомогою синтаксису .-

(.-country myobj)
;; => "FR"

Об'єкти JavaScript є змінними. У ClojureScript можна змінювати значення певних властивостей обʼєктів JavaScript за допомогою функції set!:

(set! (.-country myobj) "KR")
Перетворення

Усі розглянуті форми створення об'єктів мають один недолік: об'єкти не трансформуються рекурсивно. Це означає, що вкладені об'єкти не будуть перетворені у JavaScript. Розглянемо наступний приклад з мапами в ClojureScript та об'єктами в JavaScript:

(def clj-map {:country {:code "FR" :name "France"}})
;; => {:country {:code "FR", :name "France"}}
(:code (:country clj-map)
;; => "FR"

(def js-obj #js {:country {:code "FR" :name "France"}})
;; => #js {:country {:code "FR", :name "France"}
(.-country js-obj)
;; => {:code "FR", :name "France"}
(.-code (.-country js-obj)
;; => nil

Для вирішення цієї проблеми в ClojureScript є функції clj->js та js->clj, що перетворюють структури даних з ClojureScript у JavaScript та навпаки. Зверніть увагу на те, що під час конверсії ключове слово :country перетворюється на рядок.

(clj->js {:foo {:bar "baz"}})
;; => #js {:foo #js {:bar "baz"}}
(js->clj #js {:country {:code "FR" :name "France"}}))
;; => {"country" {:code "FR", :name "France"}}

Для перетворення вектора на масив використовується спеціалізована функція into-array:

(into-array ["France" "Korea" "Peru"])
;; => #js ["France" "Korea" "Peru"]
Масиви

У попередньому прикладі ми побачили перетворення існуючої колекції з ClojureScript на масив JavaScript. Для створення нових масивів використовують функцію make-array:

.Створення масиву довжиною 10 елементів:

(def a (make-array 10))
;; => #js [nil nil nil nil nil nil nil nil nil nil]

В ClojureScript масиви сумісні з абстракцією колекції, тому ви можете використовувати будь-які функції для перетворення масиву значень, як зі звичайними колекціями в ClojureScript. Наприклад, функція count повертає значення довжини будь-якої колекції:

(count a)
;; => 10

Масиви у JavaScript є змінною структурою даних, тому у ClojureScript також можливо записати значення масиву за певним індексом:

(aset a 0 2)
;; => 2
a
;; => #js [2 nil nil nil nil nil nil nil nil nil]

Значення масиву можна прочитати за індексом, як і в JavaScript:

(aget a 0)
;; => 2

Доступ до значень масиву по індексу та доступ до значень в мапі за ключем мають однаковий синтаксис у JavaScript, тому для роботи з об'єктами можна використовувати ті ж функції, що і для масивів::

(def b #js {:hour 16})
;; => #js {:hour 16}

(aget b "hour")
;; => 16

(aset b "minute" 22)
;; => 22

b
;; => #js {:hour 16, :minute 22}

results matching ""

    No results matching ""