console.log("Hello, world!");
;; to print in browser console (.log js/console "Hello, world!") ;; to print at ClojureScript REPL (println "Hello, world!")
// No native implementation
(ns my.library)
// No native implementation
(ns my.library (:require [other.library :as other]))
var foo = "bar";
(def foo "bar")
function foo() { var bar = 1; }
(defn foo [] (let [bar 1]))
// JavaScript "hoists" variables to the top of // their scope. So the following function: function printName() { console.log('Hello, ' + name); var name = 'Bob'; } // is equivalent to this function: function printName() { var name; console.log('Hello, ' + name); name = 'Bob'; } printName(); // Hello, undefined
;; ClojureScript does not hoist variables ;; this function will issue a warning (defn print-name [] (println "Hello, " name) (let [name "Bob"]))
// No native impelementation, must pull // aparts objects and arrays manually var o = {first: "Bob", middle: "J", last: "Smith"}; var first = o.first; var last = o.last; var middle = o.middle; ... var color = [255, 255, 100, 0.5]; var red = color[0]; var green = color[1]; var alpha = color[3]; ...
;; can always destructure in binding expression ;; including, let, function arguments, loops, etc. (def m {:first "Bob" :middle "J" :last "Smith"}) (let [{:keys [first middle last]} m] ...) (def color [255 255 100 0.5]) (let [[r g _ a] color] ...)
// can dynamic bind by putting // object in scope chain // performance implications var x = 5; var obj = { x: 10 }; with(obj) { console.log(x); // => 10 } console.log(x); // => 5
;; efficient dynamic binding (def ^:dynamic x 5) (binding [x 10] (println x)) ;; => 10 (println x) ;; => 5
// In JavaScript locals are mutable function foo(x) { x = "bar"; }
;; this will issue an error (defn foo [x] (set! x "bar"))
var a = new Array(); var a = []; var a = [1 2 3]
(def a (array)) (def a (array 1 2 3))
var o = {}; var o = new Object(); var o = {foo: 1, bar: 2};
(def o (js-obj)) (def o (js-obj "foo" 1 "bar" 2))
// No native implementation
;; efficient addition at head (def l (list)) (def l (list 1 2 3)) (def l '(1 2 3)) (conj l 4) ;; => '(4 1 2 3)
// No native implementation
;; efficient addition at the end (def v (vector)) (def v []) (def v [1 2 3]) (conj v 4) ;; => [1 2 3 4]
// No native implementation
(def s (set)) (def s #{}) (def s #{"cat" "bird" "dog"}) (conj s "cat") ;; => #{"cat" "bird" "dog"}
// No native implementation
(def m (hash-map)) (def m {}) (def m {:foo 1 :bar 2}) (conj m [:baz 3]) ;; => {:foo 1 :bar 2 :baz 3}
// map access is a static // language feature var m = { "foo": 1, "bar": 2 }; m["foo"]; m.foo; // array access is a static // language feature var a = ["red", "blue", "green"]; a[0];
// collection access is first class (def m {:foo 1 :bar 2}) (get m :foo) (def v ["red" "blue" "green"]) (nth v 0)
// Only string keys allowed var m = { "foo": 1, "bar": 2 };
;; Arbitrary keys allowed (def m { [1 2] 3 #{1 2} 3 '(1 2) 3 })
var a = []; a.push("foo"); // destructive update var b = {}; b["bar"] = 1; // destructive update
(def a []) (conj a "foo") ;; => ["foo"] ;; efficient non-destructive update (def b {}) (assoc b :bar 1) ;; => {:bar 1} ;; efficient non-destructive update
// No native implementation // array and object access // is a static language feature
(def address {:street "1 Bit Ave." :city "Bit City" :zip 10111011}) (map address [:zip :street]) ;; => (10111011 "1 Bit Ave.") ;; Collections can act as ;; functions. HashMaps are functions ;; of their keys.
var a = [...]; foo(a.slice(0)); // if foo might mutate a, must clone, // however this is only a shallow copy // no native implementation of deep copy // for primitive collections
(def a [...]) (foo a) ;; shallow cloning, deep cloning unnecessary ;; all core collections are immutable
// No native implementation var a = ["red", "blue", "green"]; var b = ["red", "blue", "green"]; console.log(a == b); // => false // == tests identity // must implement your own deep // equality test for all types
(def a ["red" "blue" "green"]) (def b ["red" "blue" "green"]) (= a b) ;; => true
var bugNumbers = [3234,4542,944,124]; if (bugNumbers.length > 0) { console.log('Not ready for release'); }
(def bug-numbers [3234 452 944 124]) (if (pos? (count bug-numbers)) (println "Not ready for release"))
var emptyString = ""; if (emptyString) { console.log("You won't see me!"); } else { console.log("Empty string is treated"+ " as false!"); }
(def empty-string "") (if empty-string (println "Empty string is not false!"))
var zero = 0; if (zero) { console.log("You won't see me!"); } else { console.log("Zero is treated as false!"); }
(def zero 0) (if zero (println "Zero is not false!"))
// == operator is coercive 1 == "1" // => true // sometimes based on value {} == {} // => false
;; ClojureScript has no coercive ;; equality operator, equality ;; is always based on value (= 1 "1") ;; => false (= {} {}) ;; => true (see Collections)
function foo() { ... return true; }
(defn foo [] true)
// JavaScript is statement oriented // explicit return function foo() { ... return true; }
;; ClojureScript is expression oriented ;; no explicit return (defn foo [] true)
var foo = function() { ... return true; }
(def foo (fn [] true))
function foo(a, b, c) { return c; }; foo(1) // => undefined foo(1, 2, 3) // => 3
(defn foo [a b c] c) (foo 1) ;; WARNING: function called with incorrect ;; number of arguments (foo 1 2 3) ;; => 3
// No native implementation must manipulate // arguments object - performance implications
(defn foo ([a] "one") ([a b] "two") ([a b c] "three")) (foo 1) ;; => "one" (foo 1 2) ;; => "two" (foo 1 2 3) ;; => "three" ;; Under advanced compilation direct dispatch to ;; arity. No arguments object manipulation
// No native implementation. Manipulate arguments // object explicitly. Performance implications. function foo() { var args = arguments; ... }
;; all arguments beyond two will be placed in a ;; sequence bound to rest (defn foo [a b & rest] ...)
// No native implementation. Pass objects // explicitly. function foo(o) { var bar = o.bar, baz = o.baz; ... } foo({bar: 1, baz: 2}); function foo(o) { var bar = o.bar || "default1", baz = o.baz || "default2"; ... }
(defn foo [& {:keys [bar baz]}] ...) (foo :bar 1 :baz 2) (defn foo [& {:keys [bar baz] :or {bar "default1" baz "default2"}}] ...)
// JavaScript does not have uniform iteration // over native types. var colors = ['red', 'orange', 'green']; for (var i = 0; i < colors.length; i++) { console.log(colors[i]); } var data = { ... }; for (var key in data) { console.log('key', key); console.log('value', data[key]); }
;; All ClojureScript datastructures support ;; uniform iterations. JavaScript natives are ;; safely extended to the same iteration protocol (def colors (array "red" "orange" "green")) (doseq [color colors] (println color)) (def colorsv ["red" "orange" "green"]) (doseq [color colorsv] (println color)) (def data { ... }) (doseq [[k v] data] (println "key" k) (println "value" v))
var callbacks = []; // A closure must be used to preserve the // return for each function at each step of // the loop. Otherwise every entry in // callbacks will return 2; for (var i = 0; i < 2; i++) { (function(_i) { callbacks.push(function() { return _i; }); })(i); } // Without the internal closure, // the result is 2 callbacks[0]() // == 0 // ECMAScript 6 can support this with // the use of blocks let callbacks = []; for (let i = 0; i < 10; i++) { let j = i; callbacks.push(function() { print(j) }); }
;; ClojureScript has proper lexical scope (def callbacks (atom [])) (dotimes [i 2] (swap! callbacks conj (fn [] i))) ((@callbacks 0)) // => 0
// this will traverse two arrays var colors = ["red", "green", "blue"]; console.log((colors.map(function(s) { return s[0]; })).map(function(c) { return c + "foo"; })); // => ["rfoo", "gfoo", "bfoo"]
;; lazy, will only traverse once (def colors ["red" "green" "blue"]) (println (map #(str % "foo") (map first colors)) ;; => ("rfoo" "gfoo" "bfoo")
var numbers = [0, 1, 2, 3, 4, 5, 7, 8, 9, 10]; var filtered = numbers.filter(function(n) { return n % 5 == 0; }); // filters entire array var firstn = filtered[0]; // but we only want the first one
(def numbers [0 1 2 3 4 5 6 7 8 9 10]) (def filtered (filter #(zero? (rem % 5)) numbers)) (def firstn (first filtered)) ;; lazy filter, values after 5 haven't ;; been looked at
function Person(name) { this.name = name; } Person.prototype.greet = function() { return "Hello, " + this.name; }
(deftype Person [name] Object (greet [_] (str "Hello" name))) ;; Constructors don't look like functions ;; No explicit prototype manipulation ;; No explicit 'this' to access fields
var person = new Person("Bob");
(Person. "Bob")
var name = "Bob"; typeof name // => "string"
;; reflection returns constructors not strings (def name "Bob") (type name) ;; => String
var name = "Bob"; name instanceof String // => true (!(name instanceof Number)) // => true
(def name "Bob") (= (type name) js/String) ;; => true (string? name) ;; => true (not= (type name) js/Number) ;; => true (not (number? name)) ;; => true
// Duck typing, implement two different types // with the same method names function Cat() {}; Cat.prototype.sound = function() { return "Meow!"; } function Dog() {}; Dog.prototype.sound = function() { return "Woof!"; } // lacking indirection no way to provide defaults (1).sound() // Error
(defprotocol ISound (sound [])) (deftype Cat [] ISound (sound [_] "Meow!")) (deftype Dog [] ISound (sound [_] "Woof!")) (extend-type default ISound (sound [_] "... silence ...")) (sound 1) ;; => "... silence ..."
var email = "test@example.com"; email.match(/@/) // => ["@"]
(def email "test@example.com") (.match email #"@") ;; => ["@"]
var invalidEmail = "f@il@example.com"; invalidEmail.match(/@/g) // => ["@", "@"]
(def invalid-email "f@il@example.com") (re-seq #"@" invalid-email) ;; => ("@" "@")
throw Error("Oops!");
(throw (js/Error. "Oops!"))
try { undefinedFunction(); } catch(e) { if (e instanceof ReferenceError) { console.log('You called a function'+ 'that does not exist'); } } finally { console.log('This runs even if'+ 'an exception is thrown'); }
(try (undefined-function) (catch js/Error e (if (= (type e) js/ReferenceError) (println (str "You called a function" "that does not exist")))) (finally (println (str "this runs even if an" "exception is thrown"))))
// JavaScript allows you to modify prototypes String.prototype.foo = function(...) { ... }; // Because of the likelihood of clashes this is // considered bad practice
;; ClojureScript namespaces everything, you ;; can make local extensions with little worry (defprotocol MyStuff (foo [this])) (extend-type string MyStuff (foo [this] ...)) ;; In addition native JavaScript objects like ;; Function, Object, Array, Number, String ;; are never actually directly extended ;; For example say you'd like to use RegExps ;; as functions (extend-type js/RegExp IFn (-invoke ([this s] (re-matches this s)))) (filter #"foo.*" ["foo" "bar" "foobar"]) ;; => ("foo" "foobar") ;; This is precisely how callable collections ;; are implemented.
// JavaScript is dynamic, standard runtime // metaprogramming techniques applicable
;; ClojureScript is dynamic, standard runtime ;; metaprogramming techniques applicable
// No native implementation, must use external // compilation tools.
;; ClojureScript has compiler macros, no external ;; tool required (defmacro my-code-transformation [...] ...) ;; Ocaml, Haskell style pattern matching is a ;; library. ;; Prolog style relational programming is a ;; library