From 134205f0b88fcc8cde416497e2fe6f3ffb202c1e Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Mon, 26 Jun 2017 12:18:38 -0500 Subject: [PATCH 1/8] initial draft: javascript-interop-ref.adoc Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 316 ++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 content/reference/javascript-interop-ref.adoc diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc new file mode 100644 index 00000000..03b43308 --- /dev/null +++ b/content/reference/javascript-interop-ref.adoc @@ -0,0 +1,316 @@ += Javascript Interop Reference +Gregg Reynolds +2017-06-25 +:type: reference +:toc: macro +:icons: font + +ifdef::env-github,env-browser[:outfilesuffix: .adoc] + +toc::[] + +[[sources]] +== Dev Notes + +This section is temporary, and will be deleted when this page is finished. + +Goals: + +* concise, complete reference, parallel to https://clojure.org/reference/java_interop[Java Interop] for Clojure +* consolidate here the interop-related info that is currently + scattered across this site (e.g. link:../about/differences.adoc[Differences from Clojure]) +* adapt Java-oriented language in the Clojure docs to JS-oriented language: +** type not class +** object not instance +** field property, not member +** method property, not method member +** etc. + +Non-goals: + +* user guide - that goes in link:../guides/javascript-interop-guide.adoc[Javascript Interop Guide] + +=== sources + +* http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/[ClojureScript: JavaScript Interop] (Rafal Spacjer blog) +* http://squirrel.pl/blog/2013/03/28/two-ways-to-access-properties-in-clojurescript/[Two Ways to Access Properties in ClojureScript] +* http://cljs.info/cheatsheet/[cljs cheatsheet] +* https://clojure.org/reference/java_interop[Clojure Java Interop] +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects[MDN Standard built-in objects] + + + +[[overview]] +== Overview + +TODO: something about dealing with different JS versions. + +See +https://clojurescript.org/reference/compiler-options#language-in-and-language-out[:language-in +and :language-out] on the Compiler Options reference page: "Configure +the input and output languages for the closure library." + + + +[[cljs-to-js]] +== Clojurescript to Javascript + +Clojurescript interop is similar to Clojure/Java interop; much of +the information on Clojure's +https://clojure.org/reference/java_interop[Java Interop] page applies +to Clojurescript, with a few caveats: + +* Javascript has object types, not classes +* Javascript "objects" are not class "instances" +* Clojurescript defines two special namespaces, `js` and `Math`. All + global variables are registered in the former; the latter is a + convenience namespace for the standard JS `Math` object. +* Javascript comes with a collection of + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects[Standard + built-in objects] that correspond to Java library classes, such as + `Math` and `String`. All except `Math` must be namespaced with `js`. +* Javascript does not have static properties + + +=== #js + +The `#js` "tagged literal" (i.e. reader tag) is the workhorse of +interop; it creates arrays and objects. + +IMPORTANT: The `#js` reader is _non-recursive_; it will not transform nested structures. + +TODO: a note about print syntax, e.g. cljs.user> #js {:a 1 :b 2} => #js {:a 1, :b 2} + + +[source,clojurescript] +---- +#js {"a" 9} +-> #js {:a 9} +#js [1 2] +-> #js [1 2] +---- + +=== JS Arrays + +There are four methods: + +* `#js [ ... ]` ;; +* `(array & args)` ;; `(array 1 2 3)` is equiv to `#js [1 2 3]` +* `(make-array sz)` ;; construct an empty javascript array of size `sz` +* `(aclone arr)` ;; shallow-copy the javascript array `arr` + +[source,clojurescript] +---- +#js [1 2] +-> #js [1 2] +(array 1 2 3) +-> #js [1 2 3] +(make-array 3) ;; create an empty array of length 3 +#js [nil nil nil] +---- + +=== JS Objects + +There are two basic ways to create a javascript object: + +* `#js { &keyvals }` +* `(js-obj &keyvals)`: create JavaSript object from an even number arguments + representing interleaved keys and values. + +[source,clojurescript] +---- +#js {:a 1 :b 2} +-> #js {:a 1, :b 2} +(js-obj :a 1 :b 2) +-> #js {::a 1, ::b 2} +---- + +==== Object Property access + +Where Java has classes, instances, members, and methods, Javascript +has (proto)types, objects, field properties, and method properties. +In the following, we will drop "property" and refer to "fields" and +"methods". + +[%hardbreaks] +**(.methodProperty object args*)** +**(.methodProperty Typename args*)** ;; FIXME: does this make sense? +**(.-fieldProperty object)** +**(Classname/staticMethod args*)** ;; FIXME: ??? +**Classname/staticField** ;; FIXME: does not apply in js? + +[source,clojure] +---- +(.toUpperCase "fred") ;; 'toUpperCase' is a method of the JS String global object +-> "FRED" +(.charAt "fred" 2) +-> "e" +(.-length "fred") ;; 'length' is a field of string "fred" +-> 4 + +Math/PI ;; Special namespace for object `Math` +-> 3.141592653589793 +(js/Date.) ;; Standard objects like `Date` are in the `js` namespace +#inst "2017-06-25T17:07:43.567-00:00" +(.getDate (js/Date.)) +25 +(.isInteger js/Number 3) ;; `Number` is another standard object +-> true +---- + +The preferred idiomatic forms for accessing field or method members are given above. The instance member form works for both fields and methods. The instanceField form is preferred for fields and required if both a field and a 0-argument method of the same name exist. They all expand into calls to the dot operator (described below) at macroexpansion time. The expansions are as follows: + +[source,clojurescript] +---- +(.methodProperty object args*) ==> (. object methodProperty args*) +(.methodProperty Typename args*) ==> + (. (identity Typename) methodProperty args*) +(.-fieldProperty object) ==> (. objec -fieldProperty) +(Typename/staticMethod args*) ==> (. Typename staticMethod args*) +Typename/staticField ==> (. Typename staticField) +---- + + +[[nested]] +=== Compound (nested) Structures + +TODO: brief note on preferring #js and/or js-obj + +The `clj->js` function recursively transforms Clojurescript values to Javascript: + +WARNING: `clj->js` is relatively inefficient; prefer other methods. + +.clj->js conversions +[cols=4] +|=== +2+| clojurescript 2+| javascript + +| set | #{} | Array | [] +| vector | [] | Array | [] +| list | () | Array | [] +| keyword | :foo | String | "foo" +| Symbol | bar | String | "bar" +| Map | {} | Object | {} +|=== + +TODO: examples + +== The Dot special form + +TODO: this is from the Clojure interop page - adapt it to cljs + +[%hardbreaks] +*(_._ instance-expr member-symbol)* +*(_._ Classname-symbol member-symbol)* +*(_._ instance-expr -field-symbol)* +*(_._ instance-expr (method-symbol args*))* or *(_._ instance-expr method-symbol args*)* +*(_._ Classname-symbol (method-symbol args*))* or *(_._ Classname-symbol method-symbol args*)* + +Special form. + +The '.' special form is the basis for access to Javascript Object +properties. It can be considered a property-access operator, and/or +read as 'in the scope of'. + +If the first operand is a symbol that resolves to a class name, the +access is considered to be to a static member of the named class. Note +that nested classes are named EnclosingClass$NestedClass, per the JVM +spec. Otherwise it is presumed to be an instance member and the first +argument is evaluated to produce the target object. + +For the special case of invoking an instance member on a Class +instance, the first argument must be an expression that evaluates to +the class instance - note that the preferred form at the top expands +`Classname` to `(identity Classname)`. + +If the second operand is a symbol and no args are supplied it is taken to be a field access - the name of the field is the name of the symbol, and the value of the expression is the value of the field, _unless_ there is a no argument public method of the same name, in which case it resolves to a call to the method. If the second operand is a symbol starting with _-_, the member-symbol will resolve only as field access (never as a 0-arity method) and should be preferred when that is the intent. + +If the second operand is a list, or args are supplied, it is taken to be a method call. The first element of the list must be a simple symbol, and the name of the method is the name of the symbol. The args, if any, are evaluated from left to right, and passed to the matching method, which is called, and its value returned. If the method has a void return type, the value of the expression will be _**nil**_. Note that placing the method name in a list with any args is optional in the canonic form, but can be useful to gather args in macros built upon the form. + +Note that boolean return values will be turned into Booleans, chars will become Characters, and numeric primitives will become Numbers unless they are immediately consumed by a method taking a primitive. + +The member access forms given at the top of this section are preferred for use in all cases other than in macros. + +'''' + +[%hardbreaks] +*(_.._ instance-expr member+)* +*(_.._ Classname-symbol member+)* + +member => fieldName-symbol or (instanceMethodName-symbol args*) + +Macro. Expands into a member access (.) of the first member on the first argument, followed by the next member on the result, etc. For instance: + +`(.. System (getProperties) (get "os.name"))` + +expands to: + +`(. (. System (getProperties)) (get "os.name"))` + +but is easier to write, read, and understand. See also the https://clojure.github.com/clojure/clojure.core-api.html#clojure.core/%2d%3e[pass:[->]] macro which can be used similarly: + +`(pass:[->] (System/getProperties) (.get "os.name"))` + +'''' + +*(_doto_ instance-expr (instanceMethodName-symbol args*)*)* + +Macro. Evaluates instance-expr then calls all of the methods/functions with the supplied arguments in succession on the resulting object, returning it. + +[source,clojure] +---- +(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2)) +-> {a=1, b=2} +---- + +[[new]] +'''' + +[%hardbreaks] +*(Classname. args*)* +*(_new_ Classname args*)* + +Special form. + +The args, if any, are evaluated from left to right, and passed to the constructor of the class named by Classname. The constructed object is returned. + +=== Alternative Macro Syntax + +As shown, in addition to the canonic special form new, Clojure supports special macroexpansion of symbols containing '.': + +`(new Classname args*)` + +can be written + +`(Classname. args*) ;note trailing dot` + +the latter expanding into the former at macro expansion time. + +'''' + +*(_instance?_ Class expr)* + +Evaluates expr and tests if it is an instance of the class. Returns true or false + +[[set]] +'''' + +[%hardbreaks] +*(_set!_ (. instance-expr instanceFieldName-symbol) expr)* +*(_set!_ (. Classname-symbol staticFieldName-symbol) expr)* + +Assignment special form. + +When the first operand is a field member access form, the assignment is to the corresponding field. If it is an instance field, the instance expr will be evaluated, then the expr. + +In all cases the value of expr is returned. + +Note - _you cannot assign to function params or local bindings. Only Java fields, Vars, Refs and Agents are mutable in Clojure_. + +'''' + + +[[js-to-cljs]] +== Javascript to Clojurescript + +=== Scopes and Namespaces From ec423c1b8d4afe8400489ff7e1ae97376a5a7a5b Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Tue, 27 Jun 2017 10:33:22 -0500 Subject: [PATCH 2/8] add some sources; add Using Javascript Libraries Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc index 03b43308..a64c2982 100644 --- a/content/reference/javascript-interop-ref.adoc +++ b/content/reference/javascript-interop-ref.adoc @@ -18,7 +18,7 @@ Goals: * concise, complete reference, parallel to https://clojure.org/reference/java_interop[Java Interop] for Clojure * consolidate here the interop-related info that is currently - scattered across this site (e.g. link:../about/differences.adoc[Differences from Clojure]) + scattered across this site (e.g. the https://github.com/clojure/clojurescript/wiki[wiki], link:../about/differences.adoc[Differences from Clojure]) * adapt Java-oriented language in the Clojure docs to JS-oriented language: ** type not class ** object not instance @@ -34,6 +34,8 @@ Non-goals: * http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/[ClojureScript: JavaScript Interop] (Rafal Spacjer blog) * http://squirrel.pl/blog/2013/03/28/two-ways-to-access-properties-in-clojurescript/[Two Ways to Access Properties in ClojureScript] +* https://github.com/cljs/api/issues/128[aget not to be used on objects] +* http://clojurescriptmadeeasy.com/blog/when-do-i-use-require-vs-import.html[When do I use :require vs :import] * http://cljs.info/cheatsheet/[cljs cheatsheet] * https://clojure.org/reference/java_interop[Clojure Java Interop] * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects[MDN Standard built-in objects] @@ -314,3 +316,23 @@ Note - _you cannot assign to function params or local bindings. Only Java fields == Javascript to Clojurescript === Scopes and Namespaces + +TODO: brief overview of namespaces in Clojure and Clojurescript + +TODO: brief explication of Google Closure namespacing mechanism + + +== Using Javascript Libraries + +=== Google Closure Library + +GCL is a massive collection of JavaScript code organized into +namespaces much like ClojureScript code itself. It is bundled with +Clojurescript; thus, you can require a namespace from GCL in the same +fashion as a ClojureScript namespace. + +TODO: a note on :require v. :import + +TODO: a few simple examples + +TODO: refer to Interop Guide for further info From f89762935b3b7b9fa26ff568634cad2a0e63fb9d Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Tue, 27 Jun 2017 10:39:12 -0500 Subject: [PATCH 3/8] add section on goog.object Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc index a64c2982..d8611ad6 100644 --- a/content/reference/javascript-interop-ref.adoc +++ b/content/reference/javascript-interop-ref.adoc @@ -160,7 +160,12 @@ Math/PI ;; Special namespace for object `Math` -> true ---- -The preferred idiomatic forms for accessing field or method members are given above. The instance member form works for both fields and methods. The instanceField form is preferred for fields and required if both a field and a 0-argument method of the same name exist. They all expand into calls to the dot operator (described below) at macroexpansion time. The expansions are as follows: +The preferred idiomatic forms for accessing field or method members +are given above. The object member form works for both fields and +methods. The objectField form is preferred for fields and required +if both a field and a 0-argument method of the same name exist. They +all expand into calls to the dot operator (described below) at +macroexpansion time. The expansions are as follows: [source,clojurescript] ---- @@ -197,15 +202,31 @@ WARNING: `clj->js` is relatively inefficient; prefer other methods. TODO: examples +=== goog.object + +TODO: make this readable + +For interacting with Javascript objects, use `goog.object` rather than `aget` + +Here are the types and the corresponding accessors you should be using: + +ILookup - get or get-in +js/Array - aget +js/Object - goog.object/get or goog.object/getValueByKeys + +Use the right function for the right type. + +(source: https://github.com/cljs/api/issues/128[aget is not to be used on objects] + == The Dot special form TODO: this is from the Clojure interop page - adapt it to cljs [%hardbreaks] -*(_._ instance-expr member-symbol)* +*(_._ object-expr member-symbol)* *(_._ Classname-symbol member-symbol)* -*(_._ instance-expr -field-symbol)* -*(_._ instance-expr (method-symbol args*))* or *(_._ instance-expr method-symbol args*)* +*(_._ object-expr -field-symbol)* +*(_._ object-expr (method-symbol args*))* or *(_._ object-expr method-symbol args*)* *(_._ Classname-symbol (method-symbol args*))* or *(_._ Classname-symbol method-symbol args*)* Special form. @@ -217,12 +238,12 @@ read as 'in the scope of'. If the first operand is a symbol that resolves to a class name, the access is considered to be to a static member of the named class. Note that nested classes are named EnclosingClass$NestedClass, per the JVM -spec. Otherwise it is presumed to be an instance member and the first +spec. Otherwise it is presumed to be an object member and the first argument is evaluated to produce the target object. -For the special case of invoking an instance member on a Class -instance, the first argument must be an expression that evaluates to -the class instance - note that the preferred form at the top expands +For the special case of invoking an object member on a Class +object, the first argument must be an expression that evaluates to +the class object - note that the preferred form at the top expands `Classname` to `(identity Classname)`. If the second operand is a symbol and no args are supplied it is taken to be a field access - the name of the field is the name of the symbol, and the value of the expression is the value of the field, _unless_ there is a no argument public method of the same name, in which case it resolves to a call to the method. If the second operand is a symbol starting with _-_, the member-symbol will resolve only as field access (never as a 0-arity method) and should be preferred when that is the intent. @@ -236,10 +257,10 @@ The member access forms given at the top of this section are preferred for use i '''' [%hardbreaks] -*(_.._ instance-expr member+)* +*(_.._ object-expr member+)* *(_.._ Classname-symbol member+)* -member => fieldName-symbol or (instanceMethodName-symbol args*) +member => fieldName-symbol or (objectMethodName-symbol args*) Macro. Expands into a member access (.) of the first member on the first argument, followed by the next member on the result, etc. For instance: @@ -255,9 +276,9 @@ but is easier to write, read, and understand. See also the https://clojure.githu '''' -*(_doto_ instance-expr (instanceMethodName-symbol args*)*)* +*(_doto_ object-expr (objectMethodName-symbol args*)*)* -Macro. Evaluates instance-expr then calls all of the methods/functions with the supplied arguments in succession on the resulting object, returning it. +Macro. Evaluates object-expr then calls all of the methods/functions with the supplied arguments in succession on the resulting object, returning it. [source,clojure] ---- @@ -298,12 +319,12 @@ Evaluates expr and tests if it is an instance of the class. Returns true or fals '''' [%hardbreaks] -*(_set!_ (. instance-expr instanceFieldName-symbol) expr)* +*(_set!_ (. object-expr objectFieldName-symbol) expr)* *(_set!_ (. Classname-symbol staticFieldName-symbol) expr)* Assignment special form. -When the first operand is a field member access form, the assignment is to the corresponding field. If it is an instance field, the instance expr will be evaluated, then the expr. +When the first operand is a field member access form, the assignment is to the corresponding field. If it is an object field, the object expr will be evaluated, then the expr. In all cases the value of expr is returned. @@ -312,6 +333,8 @@ Note - _you cannot assign to function params or local bindings. Only Java fields '''' + + [[js-to-cljs]] == Javascript to Clojurescript From 7b98d6f7cbbce6b5d880769412bb9b113c2a405c Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Tue, 27 Jun 2017 10:40:17 -0500 Subject: [PATCH 4/8] add WIP warning Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc index d8611ad6..b897a43b 100644 --- a/content/reference/javascript-interop-ref.adoc +++ b/content/reference/javascript-interop-ref.adoc @@ -7,8 +7,11 @@ Gregg Reynolds ifdef::env-github,env-browser[:outfilesuffix: .adoc] +WARNING: Work-in-Progress + toc::[] + [[sources]] == Dev Notes From cb4d208b25a523fb864bc880205a4e978f635377 Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Thu, 29 Jun 2017 13:05:20 -0500 Subject: [PATCH 5/8] add FIXMEs, content checklist, etc. Convert some of the Java lingo to Javascript (e.g. s/instance/object/) FIXME indicates places where content is missing or verified DELETE indicates code from the clj doco that i think does not apply to cljs Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 102 ++++++++++++++---- 1 file changed, 80 insertions(+), 22 deletions(-) diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc index b897a43b..31acb593 100644 --- a/content/reference/javascript-interop-ref.adoc +++ b/content/reference/javascript-interop-ref.adoc @@ -33,6 +33,31 @@ Non-goals: * user guide - that goes in link:../guides/javascript-interop-guide.adoc[Javascript Interop Guide] +=== content + +checklist: + +* namespaces +** cljs leverages Closure's mechanism +** namespace does not necessarily correspond to a source path +** js namespace +* #js +* js-obj +* array, make-array, aclone +* object property access +** dot notation +** aget/aset +** goog.object +** js-delete +* js* ?? referenced in http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/[blog] +* `instance?` equivalents: `array?`, `fn?` etc. +* external libs +** "legacy" js, embedded inline and out-of-line (i.e. non-modularized) +** modules - goog, CommonJS, AMD, ES6, etc. +* dealing with strings? +* debugging +** :repl-verbose + === sources * http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/[ClojureScript: JavaScript Interop] (Rafal Spacjer blog) @@ -227,10 +252,11 @@ TODO: this is from the Clojure interop page - adapt it to cljs [%hardbreaks] *(_._ object-expr member-symbol)* -*(_._ Classname-symbol member-symbol)* +*(_._ Typename-symbol member-symbol)* ;; FIXME clj only? *(_._ object-expr -field-symbol)* -*(_._ object-expr (method-symbol args*))* or *(_._ object-expr method-symbol args*)* -*(_._ Classname-symbol (method-symbol args*))* or *(_._ Classname-symbol method-symbol args*)* +*(_._ object-expr (method-symbol args*)) or (_._ object-expr method-symbol args) +*(_._ Typename-symbol (method-symbol args*))* or *(_._ Typename-symbol method-symbol args**) ;; FIXME clj only? +;; FIXME: get the asterisks right Special form. @@ -238,24 +264,46 @@ The '.' special form is the basis for access to Javascript Object properties. It can be considered a property-access operator, and/or read as 'in the scope of'. -If the first operand is a symbol that resolves to a class name, the -access is considered to be to a static member of the named class. Note -that nested classes are named EnclosingClass$NestedClass, per the JVM -spec. Otherwise it is presumed to be an object member and the first -argument is evaluated to produce the target object. +WARNING: DELETE (clj only?): If the first operand is a symbol that +resolves to a class name, the access is considered to be to a static +member of the named class. Note that nested classes are named +EnclosingClass$NestedClass, per the JVM spec. Otherwise it is presumed +to be an object member and the first argument is evaluated to produce +the target object. -For the special case of invoking an object member on a Class +WARNING: DELETE (clj only?) For the special case of invoking an object member on a Class object, the first argument must be an expression that evaluates to the class object - note that the preferred form at the top expands `Classname` to `(identity Classname)`. -If the second operand is a symbol and no args are supplied it is taken to be a field access - the name of the field is the name of the symbol, and the value of the expression is the value of the field, _unless_ there is a no argument public method of the same name, in which case it resolves to a call to the method. If the second operand is a symbol starting with _-_, the member-symbol will resolve only as field access (never as a 0-arity method) and should be preferred when that is the intent. - -If the second operand is a list, or args are supplied, it is taken to be a method call. The first element of the list must be a simple symbol, and the name of the method is the name of the symbol. The args, if any, are evaluated from left to right, and passed to the matching method, which is called, and its value returned. If the method has a void return type, the value of the expression will be _**nil**_. Note that placing the method name in a list with any args is optional in the canonic form, but can be useful to gather args in macros built upon the form. - -Note that boolean return values will be turned into Booleans, chars will become Characters, and numeric primitives will become Numbers unless they are immediately consumed by a method taking a primitive. - -The member access forms given at the top of this section are preferred for use in all cases other than in macros. +If the second operand is a symbol it will resolve as either a field + property reference or a method property reference. It it starts with + _`-`_, it will resolve only as field property access, never as a + 0-arity method. Otherwise it will resolve as a method property. + +NOTE: (Here's the original text from the clj docs) If the second +operand is a symbol and no args are supplied it is taken to be a field +property access - the name of the property is the name of the symbol, +and the value of the expression is the value of the property, _unless_ +there is a no argument public method of the same name, in which case +it resolves to a call to the method. + +If the second operand is a list, or args are supplied, it is taken to +be a method call. The first element of the list must be a simple +symbol, and the name of the method is the name of the symbol. The +args, if any, are evaluated from left to right, and passed to the +matching method, which is called, and its value returned. If the +method has a void return type, the value of the expression will be +_**nil**_. Note that placing the method name in a list with any args +is optional in the canonic form, but can be useful to gather args in +macros built upon the form. + +Note that boolean return values will be turned into Booleans, chars +will become Characters, and numeric primitives will become Numbers +unless they are immediately consumed by a method taking a primitive. + +The member access forms given at the top of this section are preferred +for use in all cases other than in macros. '''' @@ -279,6 +327,8 @@ but is easier to write, read, and understand. See also the https://clojure.githu '''' +WARNING: FIXME: does doto work in cljs? + *(_doto_ object-expr (objectMethodName-symbol args*)*)* Macro. Evaluates object-expr then calls all of the methods/functions with the supplied arguments in succession on the resulting object, returning it. @@ -293,8 +343,8 @@ Macro. Evaluates object-expr then calls all of the methods/functions with the su '''' [%hardbreaks] -*(Classname. args*)* -*(_new_ Classname args*)* +*(Typename. args*)* +*(_new_ Typename args*)* Special form. @@ -302,18 +352,22 @@ The args, if any, are evaluated from left to right, and passed to the constructo === Alternative Macro Syntax +FIXME: isn't this redundant? + As shown, in addition to the canonic special form new, Clojure supports special macroexpansion of symbols containing '.': -`(new Classname args*)` +`(new Typename args*)` can be written -`(Classname. args*) ;note trailing dot` +`(Typename. args*) ;note trailing dot` the latter expanding into the former at macro expansion time. '''' +FIXME: is there a cljs equivalent? Add note about `array?` etc. + *(_instance?_ Class expr)* Evaluates expr and tests if it is an instance of the class. Returns true or false @@ -322,8 +376,8 @@ Evaluates expr and tests if it is an instance of the class. Returns true or fals '''' [%hardbreaks] -*(_set!_ (. object-expr objectFieldName-symbol) expr)* -*(_set!_ (. Classname-symbol staticFieldName-symbol) expr)* +*(_set!_ (. object-expr fieldProperty-symbol) expr)* +*(_set!_ (. Classname-symbol staticFieldName-symbol) expr)* ;; FIXME: not supported in cljs? Assignment special form. @@ -333,6 +387,8 @@ In all cases the value of expr is returned. Note - _you cannot assign to function params or local bindings. Only Java fields, Vars, Refs and Agents are mutable in Clojure_. +FIXME: add aset, goog.object + '''' @@ -362,3 +418,5 @@ TODO: a note on :require v. :import TODO: a few simple examples TODO: refer to Interop Guide for further info + +TODO: refer to https://clojurescript.org/reference/dependencies[Dependencies] From 11a215ce3c70bd3b2aad6cbfa5f2a7c9eb532043 Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Wed, 12 Jul 2017 12:26:02 -0500 Subject: [PATCH 6/8] add notes re: this, getters, types & classes Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc index 31acb593..392edcd8 100644 --- a/content/reference/javascript-interop-ref.adoc +++ b/content/reference/javascript-interop-ref.adoc @@ -41,13 +41,15 @@ checklist: ** cljs leverages Closure's mechanism ** namespace does not necessarily correspond to a source path ** js namespace +* "this" - this-as, (js* "this") (see https://dev.clojure.org/display/design/this[Problem 1: ClojureScript functions as object methods]) +* goog.object as "built-in external" in ECMAScript terms * #js -* js-obj +* js- prefixed ops * array, make-array, aclone * object property access +** goog.object/geet ** dot notation -** aget/aset -** goog.object +** aget/aset (prefer goog.object/get) ** js-delete * js* ?? referenced in http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/[blog] * `instance?` equivalents: `array?`, `fn?` etc. @@ -55,11 +57,13 @@ checklist: ** "legacy" js, embedded inline and out-of-line (i.e. non-modularized) ** modules - goog, CommonJS, AMD, ES6, etc. * dealing with strings? +* use of Object in deftype: it's treated as a Protocol (for arbitrary js methods) * debugging ** :repl-verbose === sources +* https://dev.clojure.org/display/design/this[Problem 1: ClojureScript functions as object methods] * http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/[ClojureScript: JavaScript Interop] (Rafal Spacjer blog) * http://squirrel.pl/blog/2013/03/28/two-ways-to-access-properties-in-clojurescript/[Two Ways to Access Properties in ClojureScript] * https://github.com/cljs/api/issues/128[aget not to be used on objects] @@ -391,6 +395,13 @@ FIXME: add aset, goog.object '''' +=== types and classes + +[FIXME: clarify the relation between deftype/defrecord and JS types/objects] + +REFS: +* https://github.com/clojure/clojurescript/wiki/Working-with-Javascript-classes[Working with Javascript classes] +* [REF: https://stackoverflow.com/questions/9018326/how-do-i-create-an-js-object-with-methods-and-constructor-in-clojurescript[How do I create an JS Object with methods and constructor in ClojureScript] ] From b85f3baacbb545d6566915fb633799ab0d2952ea Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Wed, 12 Jul 2017 12:44:51 -0500 Subject: [PATCH 7/8] add text re: classes, types, prototypes, instance? Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc index 392edcd8..05cd8e45 100644 --- a/content/reference/javascript-interop-ref.adoc +++ b/content/reference/javascript-interop-ref.adoc @@ -41,6 +41,7 @@ checklist: ** cljs leverages Closure's mechanism ** namespace does not necessarily correspond to a source path ** js namespace +* classes, types, & prototypes - e.g. what exactly does "type" mean in cljs/js? * "this" - this-as, (js* "this") (see https://dev.clojure.org/display/design/this[Problem 1: ClojureScript functions as object methods]) * goog.object as "built-in external" in ECMAScript terms * #js @@ -61,6 +62,7 @@ checklist: * debugging ** :repl-verbose +Add: table showing mapping from JS to CLJS? e.g. intanceof -> instance, in -> js-in, etc. === sources * https://dev.clojure.org/display/design/this[Problem 1: ClojureScript functions as object methods] @@ -331,12 +333,12 @@ but is easier to write, read, and understand. See also the https://clojure.githu '''' -WARNING: FIXME: does doto work in cljs? - *(_doto_ object-expr (objectMethodName-symbol args*)*)* Macro. Evaluates object-expr then calls all of the methods/functions with the supplied arguments in succession on the resulting object, returning it. +NOTE: this example is from the https://cljs.github.io/api/cljs.core/#doto[cljs api ref] but it uses java; TODO: fix the api doc + [source,clojure] ---- (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2)) @@ -370,11 +372,17 @@ the latter expanding into the former at macro expansion time. '''' -FIXME: is there a cljs equivalent? Add note about `array?` etc. +*(_instance?_ c x)* + +Clojurescript analog to Javascript `instanceof`. Evaluates x and +tests if it is an instance of the type c. Returns true or false -*(_instance?_ Class expr)* +FIXME: what exactly does "instance of type" mean in cljs/js? express +this in js langauge, e.g. (from MDN): + +"The `instanceof` operator tests whether an object in its prototype +chain has the prototype property of a constructor." -Evaluates expr and tests if it is an instance of the class. Returns true or false [[set]] '''' @@ -397,11 +405,15 @@ FIXME: add aset, goog.object === types and classes -[FIXME: clarify the relation between deftype/defrecord and JS types/objects] +FIXME: clarify the relation between deftype/defrecord and JS +types/objects (and contrast Java classes/objects?). What is a JS "type", exactly? + +FIXME: can a deftype value be passed to JS code that expects an object? REFS: + * https://github.com/clojure/clojurescript/wiki/Working-with-Javascript-classes[Working with Javascript classes] -* [REF: https://stackoverflow.com/questions/9018326/how-do-i-create-an-js-object-with-methods-and-constructor-in-clojurescript[How do I create an JS Object with methods and constructor in ClojureScript] ] +* https://stackoverflow.com/questions/9018326/how-do-i-create-an-js-object-with-methods-and-constructor-in-clojurescript[How do I create an JS Object with methods and constructor in ClojureScript] ] From dd52d6fb5f56caf3be7ebe00c322bd2ddbdeb561 Mon Sep 17 00:00:00 2001 From: Gregg Reynolds Date: Wed, 12 Jul 2017 12:49:58 -0500 Subject: [PATCH 8/8] reorg section on js val ctors Signed-off-by: Gregg Reynolds --- content/reference/javascript-interop-ref.adoc | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/content/reference/javascript-interop-ref.adoc b/content/reference/javascript-interop-ref.adoc index 05cd8e45..e1fa50ef 100644 --- a/content/reference/javascript-interop-ref.adoc +++ b/content/reference/javascript-interop-ref.adoc @@ -63,6 +63,7 @@ checklist: ** :repl-verbose Add: table showing mapping from JS to CLJS? e.g. intanceof -> instance, in -> js-in, etc. + === sources * https://dev.clojure.org/display/design/this[Problem 1: ClojureScript functions as object methods] @@ -126,9 +127,19 @@ TODO: a note about print syntax, e.g. cljs.user> #js {:a 1 :b 2} => #js {:a 1, -> #js [1 2] ---- -=== JS Arrays +=== js* + +TODO: describe + +=== js-* + +TODO: describe the js- prefixed operators: js-arguments, js-comment, etc. + +=== JS value construction + +==== JS Arrays -There are four methods: +There are four ways to construct a JS array: * `#js [ ... ]` ;; * `(array & args)` ;; `(array 1 2 3)` is equiv to `#js [1 2 3]` @@ -145,7 +156,7 @@ There are four methods: #js [nil nil nil] ---- -=== JS Objects +==== JS Objects There are two basic ways to create a javascript object: @@ -161,7 +172,7 @@ There are two basic ways to create a javascript object: -> #js {::a 1, ::b 2} ---- -==== Object Property access +=== JS Object Property access Where Java has classes, instances, members, and methods, Javascript has (proto)types, objects, field properties, and method properties.