簡體   English   中英

JavaScript 中的多個箭頭功能是什么意思?

[英]What do multiple arrow functions mean in JavaScript?

我一直在閱讀一堆React代碼,但我看到了一些我不明白的東西:

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

那是一個咖喱 function

首先,用兩個參數檢查這個 function……

 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 function 返回一個function - 我們可以使用括號來增加清晰度。 加粗的文本是我們的add的返回值

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

換句話說, add一些數字會返回 function

 add(2) // returns (y => 2 + y)

調用柯里化函數

因此,為了使用我們的咖喱 function,我們必須稍微不同地稱呼它……

 add(2)(3) // returns 5

這是因為第一個(外部)function 調用返回第二個(內部)function。 只有調用第二個 function 后,我們才能真正得到結果。 如果我們將調用分開在兩條線上,這一點會更加明顯……

 const add2 = add(2) // returns function(y) { return 2 + y } add2(3) // returns 5

將我們的新理解應用於您的代碼

相關: “綁定、部分應用和柯里化有什么區別?”

好的,現在我們了解了它是如何工作的,讓我們看看你的代碼

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 function 正在為指定的field創建 function 。 這是一種方便的 React 技術,因為您需要在每個輸入上設置自己的偵聽器才能更新您的應用程序 state。 通過使用handleChange function,我們可以消除所有會導致為每個字段設置change偵聽器的重復代碼。 涼爽的

1這里我不必對this進行詞法綁定,因為原來的add function 沒有使用任何上下文,所以在這種情況下保留它並不重要。


更多的箭頭

如有必要,可以對兩個以上的箭頭函數進行排序 -

 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

柯里化函數能夠帶來令人驚訝的事情。 下面我們看到$定義為帶有兩個參數的柯里化 function,但在調用站點,似乎我們可以提供任意數量的 arguments。 Currying是arity的抽象 -

 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

部分應用

部分應用是一個相關的概念。 它允許我們部分應用函數,類似於柯里化,除了 function 不必以柯里化形式定義 -

 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">

簡要地

它是一個 function 返回另一個 function 以簡短的方式編寫。

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

為什么?

您是否曾經遇到過需要創建可定制的 function 的情況? 或者你有一個帶有固定參數的回調 function,但是你需要在避免全局變量的同時發送額外的變量? 如果您回答,那么這就是如何做到這一點的方法。

例如,我們有一個帶有onClick回調的按鈕。 我們想將id傳遞給 function,但是, onClick只接受一個參數event ,所以我們不能這樣做:

 const handleClick = (event, id) { event.preventDefault() // Dispatch some delete action by passing record id }

這是行不通的

作為一種解決方案,我們編寫了一個 function ,它返回另一個 function ,其變量 scope 中包含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 )

Function組成

多個箭頭函數也稱為“curried 函數”,它們用於 function 組合。

 // It is just an example, unfortunately, redux does not export dispatch function import {dispatch, compose} from 'redux' const pickSelectedUser = props => { const {selectedName, users} = props const foundUser = users.find(user => user.name === selectedName) return foundUser.id } const deleteUser = userId => event => { event.preventDefault() dispatch({ type: `DELETE_USER`, userId, }) } // The compose function creates a new function that accepts a parameter. // The parameter will be passed throw the functions from down to top. // Each function will change the value and pass it to the next function // By changing value it was not meant a mutation const handleClick = compose( deleteUser, pickSelectedUser, ) const Confirm = props => ( <div> <h1>Are you sure to delete?</h1> <button onClick={handleClick(props)}> Delete </button> </div )

一般提示:如果您對任何新的 JavaScript 語法及其編譯方式感到困惑,您可以查看Babel 例如,在 Babel 中復制您的代碼並選擇 ES 2015 預設將得到像這樣的 output

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

通天塔

了解箭頭函數的可用語法將使您了解它們在“鏈接”時引入的行為,就像您提供的示例中一樣。

當箭頭 function 被寫入時,無論有無多個參數,都會隱式返回構成函數體的表達式。 在您的示例中,該表達式是另一個箭頭 function。

 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() } | | } } | } | }

使用箭頭語法編寫匿名函數的另一個優點是它們在詞法上綁定到定義它們的 scope。 來自MDN 上的“箭頭函數”

function表達式相比,箭頭 function 表達式的語法更短,並且在詞法上綁定了this值。 箭頭函數總是匿名的。

考慮到它取自應用程序,這在您的示例中尤其相關。 正如@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) | }

可以這樣想,每次看到箭頭時,將其替換為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

它可能不完全相關,但由於提到的問題反應用例(我一直碰到這個 SO 線程):雙箭頭 function 的一個重要方面在這里沒有明確提到。 只有“第一個”箭頭(函數)被命名(因此在運行時“可區分”),任何后面的箭頭都是匿名的,從 React 的角度來看,每次渲染都算作一個“新的”object。

因此雙箭頭 function 將導致任何 PureComponent 一直重新渲染。

例子

您有一個帶有更改處理程序的父組件:

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

並使用如下渲染:

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

然后將 handleChange 用於輸入或單擊。 這一切都有效,看起來非常好。 但這意味着任何會導致父級重新渲染的更改(例如完全不相關的 state 更改)也會重新渲染您的所有 MyTask,即使它們是 PureComponents。

這可以通過多種方式得到緩解,例如傳遞“最外”箭頭和 object,您將使用它或編寫自定義 shouldUpdate function 或返回基礎知識,例如編寫命名函數(並手動綁定 this...)

您問題中的示例是使用arrow functionimplicit return第一個參數的curried function

箭頭 function 詞法綁定 this 即他們沒有自己的this參數,但從封閉的 scope 中獲取this

與上述代碼等效的是

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

關於您的示例要注意的另一件事是將handleChange定義為 const 或 function。 可能您將它用作 class 方法的一部分,它使用class fields syntax

所以不要直接綁定外部 function ,而是在 class 構造函數中綁定它

class Something{ constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange(field) { return function(e) { e.preventDefault(); // do something } } }

示例中要注意的另一件事是隱式返回和顯式返回之間的區別。

 const abc = (field) => field * 2;

上面是一個隱式返回的例子,即。 它將值字段作為參數並返回結果field*2 ,明確指定要返回的 function

對於顯式返回,您將顯式告訴方法返回值

const abc = () => { return field*2; }

關於箭頭函數需要注意的另一件事是,它們沒有自己的arguments ,但也從父級 scope 繼承。

例如,如果您只定義一個箭頭 function 像

const handleChange = () => { console.log(arguments) // would give an error on running since arguments in undefined }

作為替代箭頭函數,您可以使用 rest 參數

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:泛型

它會改變函數的類型,但結果沒有。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM