[訳]ClojureScriptの中のJavaScriptは互いに操作します。
11963 ワード
ClojureScript:JavaScript Interophttp://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/
(原文は15 th of March 2015に更新)
このブログでも言及したように、勉強を続けているClojure(とClojure Script)です。言語をよりよく理解するために、小型のWebアプリケーションを書きました。面白いために、私はすべてのフロントエンドコードをClojureScriptに書き込みます。外部JavaScript API(Bing地図AJAXコントロール)を使う必要があるので、かなり多くのJavaScriptの相互操作コードを書きました。私にとって文法は明らかではありません。これらの情報があるところが見つけられないので、この文章を書きました。これはかなり長いスレッドです。
JavaScriptの例
すべての例をより分かりやすくするために、簡単なJavaScriptコードを定義することができます。
ClojureScriptは、特別な
ClojureScriptでは、構造関数の最後に
また、オブジェクトを作成する異なる方法で、
JavaScriptメソッドを呼び出すには、メソッド名の前に
ClojureScriptはいくつかの方法JavaScriptの操作属性を提供します。最も簡単なのは、
このテーマは私にとってちょっと混乱しています。私のプロジェクトでは、このようなコードを翻訳したいです。
多くの場合、私たちはJavaScriptオブジェクトをClojureScriptから伝達する必要があります。一般的にClojureScriptは自分のデータ構造(可変ではない、持久的なvector、Map、setなど)を処理して純粋なJS対象になります。このようにするいくつかの方法があります。
もしキーパッドの値をリストに簡単なJavaScriptオブジェクトを作成するなら、
この問題を解決するために、
ある時は、JavaScriptオブジェクトまたはClojureScriptのデータ構造に変換する必要があります。私たちは
JavaScriptを使用した他のすべての方法が失敗した場合、
なお、ClojureScriptからJavaScriptコードを生成する正確な形式はコンパイラの設定に依存する。これらの設定は、Leinininingn
例えば、ClojureScriptコード:
外部JavaScriptライブラリを使用する
いつものようにコメントを賞賛します。
(原文は15 th of March 2015に更新)
このブログでも言及したように、勉強を続けているClojure(とClojure Script)です。言語をよりよく理解するために、小型のWebアプリケーションを書きました。面白いために、私はすべてのフロントエンドコードをClojureScriptに書き込みます。外部JavaScript API(Bing地図AJAXコントロール)を使う必要があるので、かなり多くのJavaScriptの相互操作コードを書きました。私にとって文法は明らかではありません。これらの情報があるところが見つけられないので、この文章を書きました。これはかなり長いスレッドです。
JavaScriptの例
すべての例をより分かりやすくするために、簡単なJavaScriptコードを定義することができます。
//global variable
globalName = "JavaScript Interop";
globalArray = globalArray = [1, 2, false, ["a", "b", "c"]];
globalObject = {
a: 1,
b: 2,
c: [10, 11, 12],
d: "some text"
};
//global function
window.hello = function() {
alert("hello!");
}
//global function
window.helloAgain = function(name) {
alert(name);
}
//a JS type
MyType = function() {
this.name = "MyType";
}
MyComplexType = function(name) {
this.name = name;
}
MyComplexType.prototype.hello = function() {
alert(this.name);
}
MyComplexType.prototype.helloFrom = function(userName) {
alert("Hello from " + userName);
}
グローバルスコープClojureScriptは、特別な
js
名前空間を定義し、JavaScriptタイプ/関数/方法/グローバルオブジェクト(すなわち、ブラウザwindow
オブジェクト)へのアクセスを許可する。(def text js/globalName)
JS出力:namespace.text = globalName;
オブジェクトを作成ClojureScriptでは、構造関数の最後に
.
を追加することにより、JavaScriptオブジェクトを作成することができます。(def t1 (js/MyType.))
JS出力:namespace.t1 = new MyType;
(注:最初はJSコードは括弧が欠けているためだと思いましたが、実際には有効です。構造関数にパラメータがない場合は括弧は省略されます。)また、オブジェクトを作成する異なる方法で、
new
関数(JSコンストラクタの名前はドット番号がないはずです)を使用します。(def my-type (new js/MyComplexType "Bob"))
JS出力:namespace.my_type = new MyComplexType("Bob");
呼び出し方法JavaScriptメソッドを呼び出すには、メソッド名の前に
.
(ポイント番号)を追加する必要があります。(.hello js/window)
JS出力:window.hello();
文法飴を抜くと:(. js/window (hello))
パラメータを私たちの関数に渡す:(.helloAgain js/window "John")
JS出力:window.helloAgain("John");
または:(. js/window (helloAgain "John"))
同じことはオブジェクトを作成することによって達成できます。(def my-type (js/MyComplexType. "Bob"))
(.hello my-type)
JS出力:namespace.my_type = new MyComplexType("Bob");
namespace.my_type.hello();
アクセスのプロパティClojureScriptはいくつかの方法JavaScriptの操作属性を提供します。最も簡単なのは、
.-
属性を使用して文法にアクセスすることである。(def my-type (js/MyType.))
(def name (.-name my-type))
JS出力:namespace.my_type = new MyType;
namespace.name = namespace.my_type.name;
同様のことは、パラメータとしてオブジェクトおよび属性の名前(文字列)を受け取るaget
関数によって達成され得る。(def name (aget my-type "name"))
JS出力:namespace.name = namespace.my_type["name"];
aget
はまた、入れ子の属性へのアクセスを許可する。(aget js/object "prop1" "prop2" "prop3")
JS出力:object["prop1"]["prop2"]["prop3"];
同じこと(生成されたコードは異なる)は、..
文法を使用して達成することができる。(.. js/object -prop1 -prop2 -prop3)
JS出力:object.prop1.prop2.prop3;
また、プロパティの値を設定することができます。ClojureScriptは、aset
またはset!
の関数を使用できます。aset
関数は、属性を文字列の名前として使用する。(def my-type (js/MyType.))
(aset my-type "name" "Bob")
JS出力:namespace.my_type["name"] = "Bob";
set!
は、属性アクセスを必要とする。(set! (.-name my-type) "Andy")
JS出力:namespace.my_type.name = "Andy";
Arayaget
関数は、JavaScript配列要素にアクセスするためにも使用され得る。(aget js/globalArray 1)
JS出力:globalArray[1];
または、入れ子の要素を取得したい場合は、このような方法で使用できます。(aget js/globalArray 3 1)
JS出力:globalArray[3][1];
ネストスコープこのテーマは私にとってちょっと混乱しています。私のプロジェクトでは、このようなコードを翻訳したいです。
var map = new Microsoft.Maps.Map();
ClojureScriptに行きます。ご覧の通りMap
関数はネストされた作用領域にあります。ネスト属性にアクセスするための慣用的な方法は、..
またはaget
関数を使用することであるが、これはコンストラクタのためには使用できない。このような状況では、私たちはポイント番号を使う必要があります。(def m2 (js/Microsoft.Maps.Themes.BingTheme.))
またはnew
関数を使用します。(def m1 (new js/Microsoft.Maps.Themes.BingTheme))
この表現を書くと:(def m3 (new (.. js/Microsoft -Maps -Themes -BingTheme)))
異常があります。First arg to new must be a symbol at line
core.clj:4403 clojure.core/ex-info
analyzer.clj:268 cljs.analyzer/error
analyzer.clj:265 cljs.analyzer/error
analyzer.clj:908 cljs.analyzer/eval1316[fn]
MultiFn.java:241 clojure.lang.MultiFn.invoke
analyzer.clj:1444 cljs.analyzer/analyze-seq
analyzer.clj:1532 cljs.analyzer/analyze[fn]
analyzer.clj:1525 cljs.analyzer/analyze
analyzer.clj:609 cljs.analyzer/eval1188[fn]
analyzer.clj:608 cljs.analyzer/eval1188[fn]
MultiFn.java:241 clojure.lang.MultiFn.invoke
analyzer.clj:1444 cljs.analyzer/analyze-seq
analyzer.clj:1532 cljs.analyzer/analyze[fn]
analyzer.clj:1525 cljs.analyzer/analyze
analyzer.clj:1520 cljs.analyzer/analyze
compiler.clj:908 cljs.compiler/compile-file*
compiler.clj:1022 cljs.compiler/compile-file
JavaScriptオブジェクトを作成多くの場合、私たちはJavaScriptオブジェクトをClojureScriptから伝達する必要があります。一般的にClojureScriptは自分のデータ構造(可変ではない、持久的なvector、Map、setなど)を処理して純粋なJS対象になります。このようにするいくつかの方法があります。
もしキーパッドの値をリストに簡単なJavaScriptオブジェクトを作成するなら、
js-obj
というマクロを使ってもいいです。(def my-object (js-obj "a" 1 "b" true "c" nil))
JS出力:namespace.my_object_4 = (function (){var obj6284 = {"a":(1),"b":true,"c":null};return obj6284;
なお、js-obj
は、キーおよびベースデータの文字列量(文字列、数字、ブール値)の値として文字列を使用するように強制される。ClojureScriptデータ構造は変わりませんので、このようにします。(def js-object (js-obj :a 1 :b [1 2 3] :c #{"d" true :e nil}))
このようなJavaScriptオブジェクトを作成します。{
":c" cljs.core.PersistentHashSet,
":b" cljs.core.PersistentVector,
":a" 1
}
使用する内部タイプが見られます。cljs.core.PersistentHashSet
cljs.core.PersistentVector
ClojureScriptのキーワードは文字列の前にコロンを付けます。この問題を解決するために、
clj-> js
関数を使用できます。「再帰的にClojureScript値をJavaScriptに変換します。Set/Vector/ListはArayとなり、KeywordとSymbolは文字列となり、MapはObjectとなる。{
"a": 1,
"b": [1, 2, 3],
"c": [null, "d", "e", true]
}
JavaScriptオブジェクトを生産する別の方法もあります。#js
reader文法を使用できます。(def js-object #js {:a 1 :b 2})
生成されたコード:namespace.core.js_object = {"b": (2), "a": (1)};
#js
を使う時、この文法は内部構造を変えないので、慎重にしてください。(def js-object #js {:a 1 :b [1 2 3] :c {"d" true :e nil}})
このようなオブジェクトを作成します。{
"c": cljs.core.PersistentArrayMap,
"b": cljs.core.PersistentVector,
"a": 1
}
この問題を解決するには、各ClojureScript構造の前に#js
を追加する必要があります。(def js-object #js {:a 1 :b #js [1 2 3] :c #js ["d" true :e nil]})
JavaScriptオブジェクト:{
"c": {
"e": null,
"d": true
},
"b": [1, 2, 3 ],
"a": 1
}
JavaScriptオブジェクトを使うある時は、JavaScriptオブジェクトまたはClojureScriptのデータ構造に変換する必要があります。私たちは
js->clj
関数を使用してこの点を達成できます。「再帰的にJavaScript配列をClojureScript Vectorに変更し、JavaScriptオブジェクトをClojure Script Mapに変更します。オブジェクトフィールドは、オプション:keywordize-key true
によって変換された文字列のKeywordからなる。(def my-array (js->clj (.-globalArray js/window)))
(def first-item (get my-array 0)) ;; 1
(def my-obj (js->clj (.-globalObject js/window)))
(def a (get my-obj "a")) ;; 1
関数としての文書で説明されているのは、作成されたMapのキーワード文字列をkeywordに変換する:keywordize-keys true
を使用することができる。(def my-obj-2 (js->clj (.-globalObject js/window) :keywordize-keys true))
(def a-2 (:a my-obj-2)) ;; 1
その他JavaScriptを使用した他のすべての方法が失敗した場合、
js*
はパラメータとして文字列を受信し、そのままJavaScriptコードとして返されます。(js* "alert('my special JS code')") ;; JS output: alert('my special JS code');
ClojureScript関数を暴露なお、ClojureScriptからJavaScriptコードを生成する正確な形式はコンパイラの設定に依存する。これらの設定は、Leinininingn
project.clj
ファイルにおいて定義されてもよい。project.clj
ファイルの関連部分::cljsbuild {
:builds [{:id "dev"
:source-paths ["src"]
:compiler {
:main your-namespace.core
:output-to "out/your-namespace.js"
:output-dir "out"
:optimizations :none
:cache-analysis true
:source-map true}}
{:id "release"
:source-paths ["src"]
:compiler {
:main blog-sc-testing.core
:output-to "out-adv/your-namespace.min.js"
:output-dir "out-adv"
:optimizations :advanced
:pretty-print false}}]}
上で定義された2つの構成が見られます。dev
とrelease
です。:optimizations
パラメータ--:advanced
を使用したコードは圧縮されます(使用されていないコードは削除されます)。例えば、ClojureScriptコード:
(defn add-numbers [a b]
(+ a b))
:advanced
モードでは、このようなJavaScriptコードにコンパイルされます。function yg(a,b){return a+b}
関数名は完全に「ランダム」ですので、JavaScriptファイルからは使えません。ClojureScript関数を使用して定義できるように、metadataとしてフラグ:export
を追加する必要があります。(defn ^:export add-numbers [a b]
(+ a b))
この:export
キーは、コンパイラに与えられた関数名が外部にエクスポートされることを教えています。これはGoogle Cloure ComplerのexportSymbol
関数によって達成されました。しかし、詳細については説明しません。そして、あなたの外部JavaScriptコードから、この関数を呼び出すことができます。your_namespace.core.add_numbers(1,2);
すべてのダッシュは、下線に変わりますので、ご注意ください。外部JavaScriptライブラリを使用する
:advanced
モードは、すべての関数/方法の名前が最小の形に変更されるので、外部ライブラリの呼び出しにも影響を及ぼします。ClojureScriptコードに来ます。Chart
オブジェクトからPolarArea
関数を呼び出します。(defn ^:export creat-chart []
(let [ch (js/Chart.)]
(. ch (PolarArea []))))
コンパイルが完了したら、コードはこうなります。function(){return(new Chart).Bc(zc)}
あなたが見ているように、PolarArea
方法はBc
に変更されました。これは当然、運行エラーを引き起こします。このような状況を防ぐために、どの名前がコンパイラに変更されてはいけないのかを教えてください。これらの名前は外部JavaScriptファイルに定義され(externs.js
)、コンパイラに提供されるべきである。私達の例ではexterns.js
ファイルはこのように見えるべきです。var Chart = {};
Chart.PolarArea = function() {};
このファイルについては、コンパイラはproject.clj
の:externs
設定で知らせるべきです。{:id "release"
:source-paths ["src"]
:compiler {
:main blog-sc-testing.core
:output-to "out-adv/your-namespace.min.js"
:output-dir "out-adv"
:optimizations :advanced
:externs ["externs.js"]
:pretty-print false}}
これらのすべてを行う場合、JavaScriptコードを作成するには、PolarArea
関数の正しい呼び出しが含まれます。function(){return(new Chart).PolarArea(Ec)}
ClojureScriptについては、外部JavaScriptライブラリを使用して、より詳細な情報を得るために、Luke VanderHartの優れた文章を読むことをお勧めします。いつものようにコメントを賞賛します。