(cljs/run-at (-> JSVM: browser) "language base")

Keywords: Attribute less Programming Javascript

Preface

Emsp; it was very exciting to know the existence of cljs more than two years ago, but because it was not used at all in the work, there was no special position in China, so the exploration of cljs was put on hold. In the past one or two years, the trend of functional programming has blown up again, which coincides with the privilege of hosting the front-end architecture of the new project. So Ramda.js is introduced to cure the long-suppressed desire in mind. Who knows it is out of control, so he abandons all interests consideration, follows his heart and pursues cljs well: Emsp; cljs is the abbreviation of ClojureScript, which is a technology that allows Clojure code to be transpile d into JavaScript code and then run on browsers or other JSVM. Because of the different host environments, only Lojure code independent of the host environment can be shared between JVM and JSVM, and cljs can not fully implement all language features in clj, let alone because JSVM is single-threaded, so it does not need STM features in clj at all... Emsp; transpile has so many functional programming for JS (e.g. Elm, PureScript), why cljs? Grammar is special, geek feeling, just as you like.

Emsp; this article will introduce the language foundation of cljs quickly, you can practice your skills directly through the Web REPL of clojurescript.net!

Notes

Emsp; first of all, introduce the way to write the annotations, which will be used in the follow-up.

A single line comment
 Single-line annotations of
 ;;; macro or defmulti single-line annotations
 ;;;;;;;; (Namespace single-line annotations)
(comment "
    multiline comment
")

#! shebang equals; a single line comment
 The u annotation follows the following expression, such as: [1 23] is actually [13], (defn test [x] (println x)) is annotated as a test function.

data type

Scalar type

; Null value/Empty set
nil

; Character string(String)
"String Data Type"

; character(Char)
\a
\newline

; Boolean type(Boolean),nil Implicit type conversion to false,0 Converting equally implicit types such as empty strings to true
true
false

; Long integer(Long)
1

; float(Float)
1.2

; Integer hexadecimal
0x0000ff

; Exponential representation
1.2e3

; key(Keyword),with:For the first character, usually for Map Act as key
:i-am-a-key

; Symbol,identifier
i-am-symbol

; Special Form
; as if, let, do etc.
(if pred then else?)
(let [a 1] expr1 expr2)
(do expr*)

Collection type

; mapping(Map),Comma prohibition between key-value pairs is used to improve readability and is essentially removable
{:k1 1, :k2 2}

; list(List)
[1 2 3]

; vector(Vector)
'(1 2 3)
; or
(list 1 2 3)

; aggregate(Set)
#{1 2 3}

On the Legal Character Set of Naming-Symbol

Symbol is an Identity in any Lisp dialect, and any identifier is limited to the range of character sets available. Then legitimate symbols should abide by the following rules:

  1. The first character cannot be [0-9:]
  2. The following characters can be [a-zA-Z0-9*+-!?]: =<>$&]
  3. The end character cannot be:

The first character is interpreted as Keyword.

Namespace

Emsp; each symbol s in cljs, whether functions or bindings, belongs to a specific namespace, so the first line of each. cljs is generally a namespace declaration.

(ns hello-world.core)

The relationship between file and namespace is one-to-one correspondence. The file paths corresponding to the above namespace are hello_word/core.cljs, hello_word/core.clj or hello_word/core.cljc. The. cljs file is used to store ClojureScript code The. clj file is used to store Clojure code or Macro code for LojureScript compiled by the JVM compiler. The. cljc file is used to store Macro code for LojureScript compiled by the CljureScript bootstrap compiler

Introducing other namespaces

Emsp; to invoke members of other namespaces, you must first introduce them

;;; Namespace A
(ns a.core)

(defn say1 []
    (println "A1"))
(defn say2 []
    (println "A2"))

;;;; Namespace B,:require Simple introduction
(ns b.core
    (:require a.core))
(a.core/say1) ;-> A1
(a.core/say2) ;-> A2

;;;; Namespace C,:as alias
(ns b.core
    (:require [a.core :as a]))
(a/say1) ;-> A1
(a/say2) ;-> A2

;;;; Namespace C,:refer Import symbol
(ns b.core
    (:require [a.core :refer [say1 say2]]))
(say1) ;-> A1
(say2) ;-> A2

Binding and functions

Emsp; cljs defaults to immutable data structures, so there is no concept of variables, instead of "binding".

binding

; Declare a global binding
(declare x)

; Define a global binding with no initialization value
(def x)

; Define a global binding with initialization values
(def x 1)

Note: Binding and functions in cljs follow the rule of declaration before use.

; Compile times Use of undeclared Var cljs.user/msg
(defn say []
    (println "say" msg))
(def msg "john")
(say)

; Declare first and compile normally
(declare msg)
(defn say []
    (println "say" msg))
(def msg "john")
(say)

function

One of the characteristics of a function is that it must have a return value, and default to the result of the last expression as the return value of the function.

; Definition
(defn Function name [PARAMETER 1 PARAMETER 2 & Uncertain parameter list]
    //Function body)

; Example 1
(defn say [a1 a2 & more]
    (println a1)
    (println a2)
    (doseq [a more]
        (print a)))
(say \1 \2 \5 \4 \3) ;Output 1 254 3

; Defining band docstrings Function
(defn Function name
    "docstrings"
    [PARAMETER 1 PARAMETER 2 & Uncertain parameter list]
    //Function body)

; Example 2
(defn say
    "Output a bunch of parameters:D"
    [a1 a2 & more]
    (println a1)
    (println a2)
    (doseq [a more]
        (print a)))

What are docstrings? Document String is used to describe functions and macro functions.

; Viewing bindings or functions docstrings
(cljs.repl/doc name)

; Example
(cljs.repl/doc say)
;;Enter the following
;; -------------------
;; cljs.user/say
;; ([a1 a2 & more])
;;   Output a bunch of parameters:D
;;=> nil
; Fuzzy search names in loaded namespaces or docstrings Matched bindings or functions docstrings
(cljs.repl/find-doc "keyword")

; Example
(cljs.repl/find-doc "A pile")
;;Enter the following
;; -------------------
;; cljs.user/say
;; ([a1 a2 & more])
;;   Output a bunch of parameters:D
;;=> nil

Digression!

; Output source code for functions in loaded namespaces
; Be careful: name Must be classpath lower.cljs Defined in the file symbol
(cljs.repl/source name)

; Example
(cljs.repl/source say)
;;Enter the following
;; -------------------
;; (defn say
;;   "Output a bunch of parameters:D"
;;   [a1 a2 & more]
;;   (println a1)
;;   (println a2)
;;   (doseq [a more]
;;     (print a)))
; In the loaded ns Search by string or regular ambiguity symbols
(cljs.repl/apropos str-or-regex)

; Example
(cljs.repl/apropos "sa")
(cljs.repl/apropos #"sa.a")
; View the public under the namespace Var
(cljs.repl/dir ns)

; Example
(cljs.repl/dir cljs.repl)
; Print the latest or specified exception object call stack information, and the latest exception object is saved in*e(One dynamic var)in
(pst)
(pst e)

Note: When we use REPL, we automatically introduce (require'[cljs.repl:refer [doc find-doc source apropos PST dir]), so we can use it directly.

Relations, Logics and Arithmetic Operational Functions

Because cljs uses prefix grammar, we are familiar with ==,!=, & amp; & amp; and + are called in the form of (= a b), (not= a b), (and 1 2) and (+1 2).

Relational Operational Function

; Values, etc., with value type conversion, and for collections, objects, the values of all elements are compared
(= a b & more)
; Numeric value, etc.
(== a b & more)

; Not equal to
(not= a b & more)

; Pointer, etc.
(identical? a b)

; Greater than, greater than or equal to, less than, less than or equal to
(> a b)
(>= a b)
(< a b)
(<= a b)
; Surprising!! JS The numeric range in the expression can only be written as 1. < x && x < 10,but cljs It can be written directly as
(< 1 x 10)
; > >= <=You can do that!

; Compare, if a less than b,Then return-1;Equal returns 0; greater returns 1.
; Concrete realization
; 1. if a,b Realized IComparable The agreement is adopted. IComparable Protocol comparison
; 2. if a and b For object, use google.array.defaultCompare
; 3. nil Used for less than other enrollment
(compare a b)

Logical Operational Function

; or
(or a & next)
; and
(and a & next)
; wrong
(not a)

The behavior of or and is consistent with that of | and & amp; & amp; under JS.

  1. In unconditional context, the or return value is the first parameter not nil or false in the parameter; and and is the last parameter not nil or false.
  2. When conditional context is used, return is implicitly converted to Boolean type.

Arithmetic operation function

; Addition,(+)Return to 0
(+ & more)

; Subtraction, or negative
(- a & more)

; Multiplication (*)Return to 1
(*)

; Divide, or take the reciprocal, denominator d Return when 0 Infinity
(/ a & more)

; Dividing, denominator d Return when 0 NaN
(quot n d)

; Self increment
(inc n)

; Self decrement
(dec n)

; Remaining, denominator d Return when 0 NaN
(rem n d)

; Mould, denominator d Return when 0 NaN
(mod n d)

The difference between redundancy and modulus is:

/**
 * @description Seeking module
 * @method mod
 * @public
 * @param {Number} o - Operands
 * @param {Number} m - Module, range of values: Numbers other than zero (integers, decimals, positive and negative)
 * @returns {Number} - The symbols of the results of modular selection are consistent with those of modular selection.
 */
var mod = (o/*perand*/, m/*odulus*/) => {
    if (0 == m) throw TypeError('argument modulus must not be zero!')
    return o - m * Math.floor(o/m)
}

/**
 * @description Seek surplus
 * @method rem
 * @public
 * @param {Number} dividend - Divisor
 * @param {Number} divisor - Divided number: Numbers other than zero (integer, decimal, positive and negative)
 * @returns {Number} remainder - Remainder, symbol and divisor symbols are consistent
 */
var rem = (dividend, divisor) => {
    if (0 == divisor) throw TypeError('argument divisor must not be zero!')
    return dividend - divisor * Math.trunc(dividend/divisor)
}

Emsp; for the second, the method provided by Math in JS will be called by the square and logarithm, etc.

; Secondary power
(js/Math.pow d e)
; Square root
(js/Math.sqrt n)

Can you notice that it's very convenient to call js methods only with js / at the beginning? According to my habit, I would label the second order with **, so I would like to set up my own method.

(defn **
    ([d e] (js/Math.pow d e))
    ([d e & more]
        (reduce ** (** d e) more)))

Process control

; if
(when test
    then)
;Example
(when (= 1 2)
    (println "1 = 2"))

; if...else...
; else?The default value is nil
(if test
    then
    else?)
;Example
(if (= 1 2)
    (println "1 = 2")
    (println "1 <> 2"))

; if...elseif..elseif...else
; expr-else The default value is nil
(cond
    test1 expr1
    test2 expr2
    :else expr-else)
;Example
(cond
    (= 1 2) (println "1 = 2")
    (= 1 3) (println "1 = 3")
    :else (println "1 <> 2 and 1 <> 3"))

; switch
; e For expressions, and test-constant For literal constants, it can be String,Number,Boolean,Keyword and Symbol Even List Equal set. e If the result of calculation is equal test-constant Value(For sets, when the depths are equal),Then it corresponds to the following result-expr Act as case The return value of the default-result-expr Arithmetic value
; If not set default-result-expr,And if the match fails, an exception will be thrown
(case expr
    test-constant1 result-expr
    test-constant2 result-expr
    ......
    default-result-expr)
;Example
(def a 1)
(case a
    1 "result1"
    {:a 2} (println 1))
; -> Return result1,And not execute println 1

; for
(loop [i start-value]
    expr
    (when (< i amount)
        (recur (inc i))))
; Example
(loop [i 0]
    (println i)
    (when (< i 10)
        (recur (inc i))))

; try...catch...finally
(try expr* catch-clause* finally-clause?)
catch-clause => (catch classname name expr*)
finally-clause? => (finally expr*)

; throw,take e-expr Operational results are thrown as exceptions
(throw e-expr)

Advanced

Interoperability with JavaScript (Interop)

cljs eventually runs on JSVM, so it inevitably interacts with JS code.

; call JS Function, the following two forms are equivalent. But notice the second, the first parameter will be used as the context of the function, and python The methods are similar.
; Best practices are the first way
(js/Math.pow 2 2)
(.pow js/Math 2 2)

; Obtain JS Object attribute values are equivalent in the following two forms.
; But note that the first one uses literal quantities to specify attribute names, which are determined when parsing.
; The second uses expressions to specify attribute names, which are determined at runtime
; Nested attributes are accessible in both ways
(.-body js/document)
(aget js/document "body")
; Example: Access nested attribute values if one of them is nil When returned directly nil,Instead of reporting anomalies
(.. js/window -document -body -firstChild) ;-> Return body The first child of an element
(aget js/window "document" "body" "firstChild") ;-> Return body The first child of an element
(.. js/window -document -body -firstChild1) ;-> Return nil,No anomalies will be reported.
(aget js/window "document" "body" "firstChild1") ;-> Return nil,No anomalies will be reported.
; Useful Ramda.js When students see this, their first feeling is not the same. R.compose(R.view, R.lensPath)Do you?^_^

; Set up JS Object attribute values are equivalent in the following two forms. Attention points are consistent with object attributes
(set! (.-href js/location) "new href")
(aset! js/location "href" "new href")

; delete JS Object attribute values
(js-delete js/location href)

; Establish JS Object, the following two forms are equivalent
#js {:a 1} ; -> {a: 1}
(js-obj {:a 1}) ; -> {a: 1}

; Establish JS Array, the following two forms are equivalent
#js [1 2]
(array 1 2)
; Create an empty array of specified length
(make-array size)
; Shallow duplicate array
(aclone arr)

; cljs Data type conversion to JS data type
; Map -> Object
(clj->js {:k1 "v1"}) ;-> {k1: "v1"}
; List -> Array
(clj->js '(1 2)) ;-> [1, 2]
; Set -> Array
(clj->js #{1 2}) ;-> [1, 2]
; Vector -> Array
(clj->js [1 2]) ;-> [1, 2]
; Keyword -> String
(clj->js :a) ;-> "a"
; Symbol -> String
(clj-js 'i-am-symbol) ;-> "i-am-symbol"

; JS Data type conversion to cljs data type
; JS Converting an array to Vector
(js->clj (js/Array. 1 2)) ;-> [1 2]
; JS Objects are converted to Map
(js->clj (clj->js {:a 1})) ;-> {"a" 1}
; JS Objects are converted to Map,Convert keys to Keyword type
(js->clj (clj->js {:a 1}) :keywordize-keys true) ;-> {:a 1}

; instantiation JS Example
; Best practices are the first way
(js/Array. 1 2) ;-> [1, 2]
(new js/Array 1 2) ;-> [1, 2]

Destructuring

Emsp; in short, declarative extraction of set elements

; Array 1 Deconstruction
(defn a [[a _ b]]
    (println a b))
(a [1 2 3]) ;-> 1 3

; Array 2 Deconstruction
(defn b [[a _ b & more]]
    (println a b (first more)))
(a [1 2 3 4 5]) ;-> 1 3 4

; Array 3 is deconstructed by:as Get the complete array
(let [[a _ b & more :as orig] [1 2 3 4 5]]
    (println {:a a, :b b, :more more, :orig orig}))
;-> {:a 1, :b 3, :more [4 5], :orig [1 2 3 4 5]}

; Key Value Pair 1 Deconstruction
; Deconstruct key-value pairs by keys, and return if no matches occur nil Or default values(adopt:or {Binding defaults}),
(let [{name :name, val :val, prop :prop :or {prop "prop1"}} {:name "name1"}]
    (println name (nil? val) prop)) ;-> "name1 true prop1"

; The key value is deconstructed by 2.:as Get the complete key-value pair
(let [{name :name :as all} {:name "name1", :val "val1"}]
    (println all)) ;-> {:name "name1", :val "val1"}

; The key value pair 3 is deconstructed, and the key type is Keyword type
(let [{:keys [name val]} {:name "name1", :val "val1"}]
    (println name val)) ;-> name1 val1

; The key value pair 4 is deconstructed, and the key type is String type
(let [{:strs [name val]} {"name" "name1", "val" "val1"}]
    (println name val)) ;-> name1 val1

; The key value pair 5 is deconstructed, and the key type is Symbol type
(let [{:syms [name val]} {'name"name1", 'val "val1"}]
    (println name val)) ;-> name1 val1

; Key Value and Array Combination Deconstruction
(let [{[a _ b] :name} {:name [1 2 3]}]
    (println a b)) ;-> 1 3

summary

Emsp; has Clojure's grammar been deeply attracted? Is there any doubt about Special Form,Symbol,Namespace, etc? Would you like to know how to use it in the project? Don't worry, we'll go deep into cljs together later. But before that, would you find that running the sample code on clojurescript.net would cause an error? The real problem is that on clojurescript.net, the next article (cljs / Run-at (JSVM.: browser) "Build a just usable development environment!" we will build a just usable development environment first and then learn more about cljs. Respect for originality, reprint from: http://www.cnblogs.com/fsjohnhuang/p/7040661.html John Fatty Boy uuuuuuuuuuuuu

Posted by assafbe on Wed, 09 Jan 2019 19:15:09 -0800