JavaScriptで複数の矢印関数はどういう意味ですか?

522
jhamm 2015-09-26 03:06.

私はたくさんのreactコードを読んでいて、私が理解していないこのようなものを見ます:

handleChange = field => e => {
  e.preventDefault();
  /// Do something here
}

7 answers

926
Thank you 2015-09-26 07:39.

それはカリー化された機能です

まず、この関数を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

別の方法で視覚化すると役立つ場合があります。矢印関数がこのように機能することはわかっています。特に戻り値に注意してみましょう。

const f = someParam => returnValue

私たちのように、add関数が返す機能を-我々は追加明確にするために括弧を使用することができます。太字のテキストは、私たちの関数の戻り値でありますadd

const add = x => (y => x + y)

言い換えればadd、いくつかの数の関数を返します

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

私たちの新しい理解をあなたのコードに適用する

関連:「バインディング、部分適用、カリー化の違いは何ですか?」

OK、それがどのように機能するかを理解したので、コードを見てみましょう

handleChange = field => e => {
  e.preventDefault()
  /// Do something here
}

矢印関数を使用せずに表現することから始めます…

handleChange = function(field) {
  return function(e) {
    e.preventDefault()
    // Do something here
    // return ...
  };
};

ただし、矢印関数は字句的にバインドされるためthis実際には次のようになります…

handleChange = function(field) {
  return function(e) {
    e.preventDefault()
    // Do something here
    // return ...
  }.bind(this)
}.bind(this)

たぶん今、私たちはこれが何をしているのかをより明確に見ることができます。handleChange関数は、指定する関数を作成していますfield。アプリケーションの状態を更新するには、入力ごとに独自のリスナーを設定する必要があるため、これは便利なReactテクニックです。このhandleChange関数を使用することで、change各フィールドにリスナーを設定する結果となる重複コードをすべて排除できます。涼しい!

1ここではthis、元のadd関数がコンテキストを使用しないため、字句的にバインドする必要はありませんでした。したがって、この場合、それを保持することは重要ではありません。


さらに多くの矢印

必要に応じて、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があなた自身のブラウザで遊ぶことができる実用的なデモです-

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 ('keydown', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">

63
sdgluck 2015-09-26 04:05.

矢印関数の使用可能な構文を理解すると、提供した例のように「チェーン」されたときにどのような動作が導入されるかを理解できます。

矢印関数がブロック中括弧なしで、複数のパラメーターの有無にかかわらず記述されている場合、関数の本体を構成する式が暗黙的に返されます。あなたの例では、その式は別の矢印関数です。

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の「矢印関数」から:

矢印関数式はに比べて短い構文を持つ関数式と辞書的に結合し、この値を。矢印関数は常に匿名です。

これは、reactjsアプリケーションから取得されていることを考えると、この例では特に適切です。@naomikによって尖ったアウトのように、中にあなたが頻繁にアクセスリアクトコンポーネントのメンバ関数を使用して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)             |  }
57
Rahil Ahmad 2018-01-02 18:47.

一般的なヒントですが、新しいJS構文のいずれかとそのコンパイル方法に混乱した場合は、babelを確認できます。たとえば、コードをbabelでコピーし、es2015プリセットを選択すると、次のような出力が得られます。

handleChange = function handleChange(field) {
 return function (e) {
 e.preventDefault();
  // Do something here
   };
 };

47
LifeQuery 2015-09-26 04:02.

このように考えてください。矢印が表示されるたびに、それを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
37
sultan 2019-02-08 06:34.

簡潔でシンプル🎈

短く書かれた別の関数を返す関数です。

const handleChange = field => e => {
  e.preventDefault()
  // Do something here
}

// is equal to 
function handleChange(field) {
  return function(e) {
    e.preventDefault()
    // Do something here
  }
}

なぜ人々はそれをするのか

カスタマイズ可能な関数を書く必要があるときに直面したことがありますか?または、固定パラメーター(引数)を持つコールバック関数を作成する必要がありますが、関数にさらに多くの変数を渡す必要がありますが、グローバル変数は避けますか?あなたの答えが「はい」の場合、それはそれを行う方法です。

たとえばbutton、withonClickコールバックがあります。そしてid、関数に渡す必要がありonClickますが、受け入れるパラメーターeventは1つだけであり、次のように追加のパラメーターを渡すことはできません。

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
)

3
Don Kartacs 2019-11-16 04:31.

それは完全に関連しているわけではないかもしれませんが、reactのユースケースに言及されているので(そして私はこのSOスレッドにぶつかり続けます):ここで明示的に言及されていない二重矢印関数の1つの重要な側面があります。「最初の」矢印(関数)のみに名前が付けられ(したがって、実行時に「区別可能」になります)、後続の矢印は匿名であり、Reactの観点からはすべてのレンダリングで「新しい」オブジェクトとしてカウントされます。

したがって、二重矢印関数を使用すると、PureComponentが常に再レンダリングされます。

次のような変更ハンドラを持つ親コンポーネントがあります。

handleChange = task => event => { ... operations which uses both task and event... };

そして次のようなレンダリングで:

{ tasks.map(task => <MyTask handleChange={this.handleChange(task)}/> }

次に、handleChangeを入力またはクリックで使用します。そして、これはすべて機能し、とても素敵に見えます。ただし、親を再レンダリングする変更(完全に無関係な状態変更など)は、PureComponentsであっても、すべてのMyTaskも再レンダリングすることを意味します。

これは、「最も外側の」矢印とそれをフィードするオブジェクトを渡す、カスタムのshouldUpdate関数を作成する、名前付き関数を作成する(そしてこれを手動でバインドするなど)基本に戻るなど、さまざまな方法で軽減できます。

1
Shubham Khatri 2019-02-07 20:08.

あなたの質問の例は、最初の引数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;

上記は暗黙的なリターンの例です。値フィールドを引数として受け取り、返す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);
}

Related questions

MORE COOL STUFF

「水曜日」シーズン1の中心には大きなミステリーがあります

「水曜日」シーズン1の中心には大きなミステリーがあります

Netflixの「水曜日」は、典型的な10代のドラマ以上のものであり、実際、シーズン1にはその中心に大きなミステリーがあります.

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ボディーランゲージの専門家は、州訪問中にカミラ・パーカー・ボウルズが輝くことを可能にした微妙なケイト・ミドルトンの動きを指摘しています

ケイト・ミドルトンは、州の夕食会と州の訪問中にカミラ・パーカー・ボウルズからスポットライトを奪いたくなかった、と専門家は言う.

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンがハリー・スタイルズとオリビア・ワイルドの「非常に友好的な」休憩が永続的であることを望んでいる理由

一部のファンが、オリビア・ワイルドが彼女とハリー・スタイルズとの間の「難しい」が「非常に友好的」な分割を恒久的にすることを望んでいる理由を見つけてください.

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする 

エリザベス女王の死後、ケイト・ミドルトンはまだ「非常に困難な時期」を過ごしている、と王室の専門家が明らかにする&nbsp;

エリザベス女王の死後、ケイト・ミドルトンが舞台裏で「非常に困難な時期」を過ごしていたと伝えられている理由を調べてください.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

パンデミックは終わったかもしれないが、Covid-19 は終わっていない

2021 年 6 月 8 日にニューヨーク市で開催された covid-19 パンデミックで亡くなった人々の命を偲び、祝うために、ネーミング ザ ロスト メモリアルズが主催するイベントと行進の最中に、グリーンウッド墓地の正門から記念碑がぶら下がっています。週末、ジョー・バイデン大統領は、covid-19 パンデミックの終息を宣言しました。これは、過去 2 年以上にわたり、公の場でそうするための長い列の中で最新のものです。

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

デビル・イン・オハイオの予告編は、エミリー・デシャネルもオハイオにいることを明らかにしています

オハイオ州のエミリー・デシャネル みんな早く来て、ボーンズが帰ってきた!まあ、ショーボーンズではなく、彼女を演じた俳優. エミリー・デシャネルに最後に会ってからしばらく経ちました.Emily Deschanel は、長期にわたるプロシージャルな Bones の Temperance “Bones” Brennan としてよく知られています。

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

ドナルド・トランプはFBIのマー・ア・ラーゴ襲撃映像をリリースする予定ですか?

どうやら、ドナルド・トランプに近い人々は、今月初めにFBIによって家宅捜索された彼のMar-a-Lago財産からの映像を公開するよう彼に勧めています. 前大統領はテープを公開するかどうかを確認していませんが、息子はフォックス・ニュースにそうなるだろうと語った.

Andor は、他の Star Wars ショーから大きな距離を置きます。

Andor は、他の Star Wars ショーから大きな距離を置きます。

アンドールの一場面。数十年前、ジョージ・ルーカスがスター・ウォーズのテレビ番組を制作するのを妨げた主な理由は、お金でした。

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、彼らが子供たちと行っているスパイをテーマにした活動を共有しています

ケイト・ミドルトンとウィリアム王子は、子供向けのパズルの本の序文を書き、ジョージ王子、シャーロット王女、ルイ王子と一緒にテキストを読むと述べた.

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

事故で押しつぶされたスイカは、動物を喜ばせ水分補給するために野生生物保護団体に寄付されました

Yak's Produce は、数十個のつぶれたメロンを野生動物のリハビリ専門家であるレスリー グリーンと彼女のルイジアナ州の救助施設で暮らす 42 匹の動物に寄付しました。

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

デミ・ロヴァートは、新しいミュージシャンのボーイフレンドと「幸せで健康的な関係」にあります: ソース

8 枚目のスタジオ アルバムのリリースに向けて準備を進めているデミ ロヴァートは、「スーパー グレート ガイ」と付き合っている、と情報筋は PEOPLE に確認しています。

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

Plathville の Kim と Olivia Plath が数年ぶりに言葉を交わすことへようこそ

イーサン プラスの誕生日のお祝いは、TLC のウェルカム トゥ プラスビルのシーズン 4 のフィナーレで、戦争中の母親のキム プラスと妻のオリビア プラスを結びつけました。

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

仕事の生産性を高める 8 つのシンプルなホーム オフィスのセットアップのアイデア

ホームオフィスのセットアップ術を極めよう!AppExert の開発者は、家族全員が一緒にいる場合でも、在宅勤務の技術を習得しています。祖父や曽祖父が共同家族で暮らしていた頃の記憶がよみがえりました。

2022 年、私たちのデジタル ライフはどこで終わり、「リアル ライフ」はどこから始まるのでしょうか?

20 年前のタイムトラベラーでさえ、日常生活におけるデジタルおよびインターネットベースのサービスの重要性に驚くことでしょう。MySpace、eBay、Napster などのプラットフォームは、高速化に焦点を合わせた世界がどのようなものになるかを示してくれました。

ニューロマーケティングの秘密科学

ニューロマーケティングの秘密科学

マーケティング担当者が人間の欲望を操作するために使用する、最先端の (気味が悪いと言う人もいます) メソッドを探ります。カートをいっぱいにして 3 桁の領収書を持って店を出る前に、ほんの数点の商品を買いに行ったことはありませんか? あなたは一人じゃない。

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

地理情報システムの日: GIS 開発者として学ぶべき最高の技術スタック

私たちが住んでいる世界を確実に理解するには、データが必要です。ただし、空間参照がない場合、このデータは地理的コンテキストがないと役に立たなくなる可能性があります。

Language