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