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 |