私はたくさんのreact
コードを読んでいます、そして私は理解できないようなものを見ます:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
これは カリー化関数
まず、2つのパラメータを使ってこの関数を調べます…
const add = (x, y) => x + y
add(2, 3) //=> 5
ここはまたカレーの形です…
const add = x => y => x + y
これは同じです1 矢印機能なしのコード…
const add = function (x) {
return function (y) {
return x + y
}
}
return
NAMEに注目_
それは別の方法でそれを視覚化するのを助けるかもしれません。矢印関数はこのように機能することがわかっています - 戻り値に特に注意しましょう。
const f = someParam => returnValue
だから私たちのadd
name__関数はfunctionを返します - わかりやすくするために括弧を使うことができます。 太字のテキストは、関数add
name__の戻り値です。
const add = x => (y => x + y)
言い換えれば、ある数のadd
name__は関数を返します。
add(2) // returns (y => 2 + y)
カリー化関数の呼び出し
だから我々のカレー関数を使うためには、我々はそれを少し違う呼び方をしなければならない…
add(2)(3) // returns 5
これは、最初の(外側の)関数呼び出しが2番目の(内側の)関数を返すためです。 2番目の関数を呼び出した後に初めて、実際に結果が得られます。これは、呼び出しを2行に分けるともっと明白になります。
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
私たちの新しい理解をあなたのコードに適用する
related: "製本、部分塗布、カレーの違いは何ですか?"
それでは、その仕組みが理解できたので、コードを見てみましょう。
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
矢印関数を使わずにそれを表現することから始めましょう…
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
しかし、矢印関数はthis
name__をレキシカルにバインドするため、実際にはのようになります。
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
たぶん今、私たちはこれが何をしているのかもっと明確に見ることができます。 handleChange
name__関数は、指定されたfield
name__に対して関数を作成しています。アプリケーションの状態を更新するには、各入力に独自のリスナーを設定する必要があるため、これは便利なReact手法です。 handleChange
name__関数を使用することで、各フィールドにchange
name__リスナーを設定することになる重複コードをすべて排除できます。クール!
1 元のthis
name__関数はコンテキストを使用していないため、ここではadd
name__をレキシカルにバインドする必要はありませんでした。したがって、この場合は保存することは重要ではありません。
さらに矢印
必要に応じて、3つ以上の矢印関数を順番に並べることができます -
const three = a => b => c =>
a + b + c
const four = a => b => c => d =>
a + b + c + d
three (1) (2) (3) // 6
four (1) (2) (3) (4) // 10
カリー化された関数は驚くべきことが可能です。以下では$
が2つのパラメータを持つカリー化関数として定義されているのを見ていますが、呼び出しサイトでは、引数をいくつでも指定できるように見えます。カレー化は アリティ の抽象化です -
const $ = x => k =>
$ (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
$ (1) // 1
(add (2)) // + 2 = 3
(mult (6)) // * 6 = 18
(console.log) // 18
$ (7) // 7
(add (1)) // + 1 = 8
(mult (8)) // * 8 = 64
(mult (2)) // * 2 = 128
(mult (2)) // * 2 = 256
(console.log) // 256
部分適用
部分適用は関連する概念です。それは私たちが部分的に関数をカレー化形式で定義する必要がないことを除いて、カレー化と同様に関数を適用することを可能にします -
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const add3 = (x, y, z) =>
x + y + z
partial (add3) (1, 2, 3) // 6
partial (add3, 1) (2, 3) // 6
partial (add3, 1, 2) (3) // 6
partial (add3, 1, 2, 3) () // 6
partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3
これはあなたがあなた自身のブラウザで遊ぶことができるpartial
name__の実用的なデモです -
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const preventDefault = (f, event) =>
( event .preventDefault ()
, f (event)
)
const logKeypress = event =>
console .log (event.which)
document
.querySelector ('input[name=foo]')
.addEventListener ('keypress', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">
の利用可能な矢印関数 の構文を理解することは、あなたが提供した例のように「連鎖」したときにそれらがどのような振る舞いを導入するかの理解をあなたに与えるでしょう。
複数のパラメータを使用して、または使用せずに、ブロック括弧なしで矢印関数を書くと、その関数の本体を構成する式は暗黙的にが返されます。あなたの例では、その表現はもう一つの矢印関数です。
No arrow funcs Implicitly return `e=>{…}` Explicitly return `e=>{…}`
---------------------------------------------------------------------------------
function (field) { | field => e => { | field => {
return function (e) { | | return e => {
e.preventDefault() | e.preventDefault() | e.preventDefault()
} | | }
} | } | }
矢印構文を使用して無名関数を書くことのもう1つの利点は、それらが定義されている範囲に字句的に結び付けられることです。 MDNの「矢印機能」 から:
矢印関数式は、 関数式 に比べて構文が短く、 this の値を辞書的にバインドします。矢印関数は常に anonymous です。
これは reactjs アプリケーションから取得されることを考えると、あなたの例では特に適切です。 @naomikが指摘したように、Reactではthis
を使って コンポーネントのメンバー関数 にアクセスすることがよくあります。例えば:
Unbound Explicitly bound Implicitly bound
------------------------------------------------------------------------------
function (field) { | function (field) { | field => e => {
return function (e) { | return function (e) { |
this.setState(...) | this.setState(...) | this.setState(...)
} | }.bind(this) |
} | }.bind(this) | }
一般的なヒントとして、新しいJSの構文とそのコンパイル方法に混乱した場合は、 babel を確認してください。たとえば、コードをbabelでコピーしてes2015プリセットを選択すると、次のような出力が得られます。
handleChange = function handleChange(field) {
return function (e) {
e.preventDefault();
// Do something here
};
};
このように考えてください、あなたが矢印を見るたびに、あなたはそれをfunction
に置き換えます。function parameters
は矢印の前に定義されています。
だからあなたの例では:
field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}
そして一緒に:
function (field) {
return function (e) {
e.preventDefault();
};
}
ドキュメントから :
// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there's only one argument:
singleParam => { statements }
singleParam => expression
簡潔で単純な????
簡単に書かれた別の関数を返す関数です。
const handleChange = field => e => {
e.preventDefault()
// Do something here
}
// is equal to
function handleChange(field) {
return function(e) {
e.preventDefault()
// Do something here
}
}
なぜ人々はそれをするのか❓
カスタマイズ可能な関数を書く必要があるときに直面しましたか?あるいは、固定されたパラメータ(引数)を持つコールバック関数を書く必要がありますが、グローバル変数を避けながら、より多くの変数を関数に渡す必要がありますか?あなたの答えが "yes"ならそれはそれをする方法です。
たとえば、onClickコールバックを持つbutton
があります。 id
を関数に渡す必要がありますが、onClick
は1つのパラメータevent
のみを受け入れます。このように追加のパラメータを渡すことはできません。
const handleClick = (event, id) {
event.preventDefault()
// Dispatch some delete action by passing record id
}
うまくいかないだろう!
したがって、グローバル変数は悪であるため、グローバル変数を使用せずに独自の変数スコープを持つ他の関数を返すような関数を作成します。
関数の下ではhandleClick(props.id)}
が呼び出されて関数を返し、そのスコープ内にid
があります。何度押されても、IDが互いに影響したり変更したりしなくても、完全に独立しています。
const handleClick = id => event {
event.preventDefault()
// Dispatch some delete action by passing record id
}
const Confirm = props => (
<div>
<h1>Are you sure to delete?</h1>
<button onClick={handleClick(props.id)}>
Delete
</button>
</div
)
あなたの質問の例はcurried function
を利用し、最初の引数にarrow function
を持つimplicit return
の例です。
矢印関数はこれを語彙的に束縛します、すなわちそれらは彼ら自身のthis
引数を持っていませんが囲んでいるスコープからthis
値を取ります
上記のコードと同等のものになります
const handleChange = (field) {
return function(e) {
e.preventDefault();
/// Do something here
}.bind(this);
}.bind(this);
あなたの例に関してもう1つ注意すべきことは、handleChange
をconstまたは関数として定義することです。おそらくあなたはクラスメソッドの一部としてそれを使っていて、それはclass fields syntax
を使っています
そのため、外部関数を直接バインドするのではなく、クラスコンストラクターでバインドします。
class Something{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(field) {
return function(e) {
e.preventDefault();
// do something
}
}
}
この例で注意すべきもう1つのことは、暗黙的リターンと明示的リターンの違いです。
const abc = (field) => field * 2;
上記は暗黙のリターンの例です。引数としてvalueフィールドを取り、返す関数を明示的に指定した結果field*2
を返します。
明示的に返すには、メソッドに値を返すように明示的に指示します。
const abc = () => { return field*2; }
矢印関数について注意すべきもう1つのことは、それらが独自のarguments
を持っていないが、それを両親のスコープからも継承するということです。
たとえば、単に次のように矢印関数を定義したとします。
const handleChange = () => {
console.log(arguments) // would give an error on running since arguments in undefined
}
代替の矢印機能として、あなたが使用できる残りのパラメータを提供します。
const handleChange = (...args) => {
console.log(args);
}
var handleChange = field => e => {
e.preventDefault();
/// Do something here
}
Ecma5では、翻訳:
"use strict";
var handleChange = function handleChange(field) {
return function (e) {
e.preventDefault(); /// Do something here
};
};
var f = function(x,y) { return x+y }
var g = function(x) { return function(y) { return x+y }}
f: (T x T) -> T
g: T -> T -> T
T:ジェネリック型
関数の種類は変わりますが、結果はNOになります。