Javascript'te çoklu ok işlevleri ne anlama geliyor?


472

Bir sürü reactkod okuyordum ve anlamadığım bu gibi şeyler görüyorum:

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

11
Sadece eğlence için, Kyle Simpson oklar için tüm karar yollarını bu akış şemasına koydu . Kaynak: ES6'da Derinlik: Ok fonksiyonları başlıklı bir Mozilla Hacks blog yazısı hakkındaki yorumu
gfullam

Büyük cevaplar var ve şimdi bir lütuf var. Lütfen aşağıdaki cevapların kapsamadığını anlamadığınız şeyleri ayrıntılı olarak açıklar mısınız?
Michael Warner

5
Ok işlevleri akış şemasının URL'si artık kırıldı çünkü kitabın yeni bir sürümü var. Çalışma URL'si raw.githubusercontent.com/getify/You-Dont-Know-JS/1st-ed/… adresinde
Dhiraj Gupta

Yanıtlar:


831

Bu körelmiş bir işlev

İlk olarak, bu fonksiyonu iki parametre ile inceleyin…

const add = (x, y) => x + y
add(2, 3) //=> 5

İşte yine kıvrımlı formda…

const add = x => y => x + y

İşte aynı 1 kodu ok fonksiyonları olmadan…

const add = function (x) {
  return function (y) {
    return x + y
  }
}

Odaklan return

Başka bir şekilde görselleştirmeye yardımcı olabilir. Ok işlevlerinin bu şekilde çalıştığını biliyoruz - geri dönüş değerine özellikle dikkat edelim .

const f = someParam => returnValue

Bizim Yani addişlevi döndüren işlevi - eklediğimiz netlik için parantezleri kullanabilirsiniz. Kalın metin bizim işlevin dönüş değeriadd

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

Başka bir deyişle add, bazı sayılar bir işlev döndürür

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

Kıvrımlı işlevleri çağırma

Körili fonksiyonumuzu kullanabilmek için biraz farklı çağırmalıyız…

add(2)(3)  // returns 5

Bunun nedeni, ilk (dış) işlev çağrısının ikinci (iç) bir işlevi döndürmesidir. Sadece ikinci işlevi çağırdıktan sonra sonucu elde ederiz. Çağrıları iki hatta ayırırsak bu daha belirgindir…

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

Yeni anlayışımızı kodunuza uygulama

related: “Bağlama, kısmi uygulama ve körelme arasındaki fark nedir?”

Tamam, şimdi bunun nasıl çalıştığını anladığımıza göre, kodunuza bakalım

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

Ok işlevlerini kullanmadan temsil ederek başlayacağız…

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

Ancak, ok işlevleri sözlüksel olarak bağlandığından this, aslında daha çok şöyle görünecektir…

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

Belki şimdi bunun ne yaptığını daha net görebiliyoruz. handleChangeFonksiyon belirtilen bir işlev yaratıyor field. Bu kullanışlı bir React tekniğidir, çünkü uygulamalarınızın durumunu güncellemek için her girişte kendi dinleyicilerinizi kurmanız gerekir. handleChangeİşlevi kullanarak, changeher alan için dinleyici ayarlanmasına neden olacak tüm yinelenen kodu kaldırabiliriz . Güzel!

1 Burada thisorijinal addişlev herhangi bir bağlam kullanmadığı için sözcüksel olarak bağlanmak zorunda kalmadım , bu nedenle bu durumda korumak önemli değil.


Daha da fazla ok

Gerekirse ikiden fazla ok işlevi sıralanabilir -

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

Körili işlevler şaşırtıcı şeyler yapabilir. Aşağıda $, iki parametreli bir curried fonksiyon olarak tanımlandığını görüyoruz , ancak çağrı sitesinde, herhangi bir sayıda argüman sağlayabiliriz gibi görünüyor. Currying ait soyutlamadır 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

Kısmi uygulama

Kısmi uygulama ilgili bir kavramdır. Fonksiyonun curried formda tanımlanması gerekmemesi dışında, köriye benzer fonksiyonları kısmen uygulamamıza izin verir -

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

İşte partialkendi tarayıcınızda oynayabileceğiniz çalışan bir demo -

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


2
Bu olağanüstü! Birisi aslında '$' değerini ne sıklıkla atar? Yoksa tepki için bir takma ad mı? Cehaletimi son olarak affet, sadece merak ediyorum çünkü diğer dillerde çok sık görev alan bir sembol görmüyorum.
Caperneoignis

7
@Caperneoignis $konsepti tanıtmak için kullanıldı, ancak istediğinizi adlandırabilirsiniz. Tesadüfen ama tamamen ilgisiz, $ olan jQuery, gibi sevilen kütüphanelerde kullanılan $sıralama fonksiyonların tamamını kütüphanesine küresel erişim noktası taşımaktadır. Bence başkalarında da kullanılmış. Gördüğünüz bir diğeri _, alt çizgi ve lodash gibi kütüphanelerde popüler. Hiçbir sembol diğerinden daha anlamlı değildir; Eğer için anlamını atamak sizin programa. Basitçe geçerli JavaScript: D
Teşekkür ederim

1
kutsal frijoli, güzel cevap.
op'un

2
@Blake $Nasıl kullanıldığına bakarak daha iyi bir anlayış kazanabilirsiniz . Uygulamanın kendisini soruyorsanız, $değer alan xve yeni bir işlev döndüren bir işlevdir k => .... İade fonksiyonun gövdesinde baktığımızda, gördüğümüz k (x)bildiğimiz bu yüzden kde bir fonksiyon olmalıdır ve sonucu ne olursa olsun k (x)geri koymak alır edilir $ (...), biz döner başka biliyoruz k => ...ve ona gider ... hala iseniz takılmak, bana bildirin.
Teşekkürler

2
bu cevap nasıl çalıştığını ve bu teknikle hangi kalıpların olduğunu açıkladı. Bunun neden herhangi bir senaryoda daha iyi bir çözüm olduğuna dair özel bir şey olmadığını hissediyorum. Hangi durumda, abc(1,2,3)idealden daha azdır abc(1)(2)(3). Kod mantığı hakkında zorlamak ve abc fonksiyonunu okumak zor ve fonksiyon çağrısını okumak daha zor. Sadece abc'nin ne yaptığını bilmeden önce, şimdi abc'nin hangi isimsiz işlevlerin geri döndüğünden emin değilsiniz ve iki kez.
Muhammed Umer

57

Anlamak ok fonksiyonlarının mevcut sözdizimi size 'zincirleme' örneklerde Sağladığınız gibi zaman tanıtan ne davranış bir anlayış verecektir.

Bir ok işlevi blok parantezleri olmadan, birden fazla parametre ile veya birden çok parametre olmadan yazıldığında, işlevin gövdesini oluşturan ifade dolaylı olarak döndürülür. Örneğin, bu ifade başka bir ok işlevidir.

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

Ok sözdizimini kullanarak anonim işlevler yazmanın bir diğer avantajı, tanımlandıkları kapsama sözcüksel olarak bağlı olmalarıdır. MDN'deki 'Ok işlevleri' nden :

Bir ok işlevi, ifade ile karşılaştırıldığında daha kısa bir söz dizimi olan işlev ifadeleri ve lexically bağlanan bu değer. Ok işlevleri her zaman anonimdir .

Bu, örneğinizde özellikle bir uygulama. @ Naomik tarafından işaret edildiği gibi, React'te sıklıkla bir bileşenin üye işlevlerine kullanarak erişirsiniz this. Örneğin:

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

53

Genel bir ipucu, yeni JS sözdizimi ve nasıl derleneceği konusunda kafanız karışırsa, babel'i kontrol edebilirsiniz . Örneğin, kodunuzu babil'e kopyalamak ve es2015 ön ayarını seçmek, böyle bir çıktı verir

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

Babil


42

Bunu şöyle düşünün, her ok gördüğünüzde onun yerini alırsınız function.
function parametersoktan önce tanımlanır.
Yani örneğinizde:

field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}

ve sonra birlikte:

function (field) { 
    return function (e) { 
        e.preventDefault(); 
    };
}

Dokümanlardan :

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

6
Sözcüksel bağdan bahsetmeyi unutmayın this.
Teşekkürler

30

Kısa ve basit 🎈

Kısa bir şekilde yazılmış başka bir işlevi döndüren bir işlevdir.

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

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

İnsanlar neden bunu yapıyor

Özelleştirilebilen bir işlev yazmanız gerektiğinde karşılaştınız mı? Veya sabit parametreleri (bağımsız değişkenler) olan bir geri çağırma işlevi yazmak zorundasınız, ancak işleve daha fazla değişken iletmeniz gerekiyor, ancak genel değişkenlerden kaçınıyor musunuz? Cevabınız " evet " ise, bunu yapmanın yolu budur.

Örneğin, bir buttononClick geri araması var . Ve idfonksiyona geçmemiz gerekiyor , ancak onClicksadece bir parametreyi kabul ediyor event, bu şekilde ekstra parametreler geçiremiyoruz:

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

Çalışmayacak!

Bu nedenle, global değişkenler kötülük 😈 olduğundan, diğer değişkenleri kendi değişkenleri kapsamı ile başka bir işlev döndürecek bir işlev yaparız.

Aşağıda fonksiyon handleClick(props.id)}çağrılır ve bir fonksiyon döndürülür idve kapsamı içinde olur! Kaç kez basılacağına bakılmaksızın, id'ler birbirini etkilemez veya değiştirmez, tamamen izole edilirler.

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
)

2

Sorunuzdaki örnek , ilk argüman için curried functionkullanılan arrow functionve bir olan bir implicit return.

Ok işlevi sözlü olarak bunu bağlar, yani kendi thisargümanları yoktur, ancak thisdeğeri ekteki kapsamdan alırlar

Yukarıdaki kodun eşdeğeri,

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

Örneğiniz hakkında dikkat edilmesi gereken bir şey daha handleChange, bir sabit veya işlev olarak tanımlanmasıdır. Muhtemelen bir sınıf yönteminin parçası olarak kullanıyorsunuz veclass fields syntax

yani dış işlevi doğrudan bağlamak yerine, sınıf yapıcısına bağlarsınız

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

Örnekte dikkat edilmesi gereken başka bir şey, örtük ve açık dönüş arasındaki farktır.

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

Yukarıda örtük getiri örneği örnektir. değer alanını bağımsız değişken olarak alır ve döndürülecek field*2işlevi açıkça belirten sonucu döndürür

Açık bir getiri için, açıkça değeri döndürme yöntemini söylerdiniz

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

Ok işlevleri ile ilgili not edilmesi gereken bir başka şey de, kendilerine sahip olmamaları, argumentsancak ebeveynlerin kapsamından da miras almalarıdır.

Örneğin, sadece bir ok işlevi

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

Alternatif bir ok fonksiyonu olarak kullanabileceğiniz dinlenme parametreleri

const handleChange = (...args) => {
   console.log(args);
}

1

Tamamen ilgili olmayabilir, ancak bahsedilen soru davayı kullandığından (ve bu SO iş parçacığına çarpmaya devam ediyorum): Burada açıkça belirtilmeyen çift ok işlevinin önemli bir yönü var. Yalnızca 'ilk' ok (işlev) adlandırılır (ve böylece çalışma zamanı tarafından 'ayırt edilebilir'), aşağıdaki oklar anonimdir ve React bakış açısından her render'da 'yeni' nesne olarak sayılır.

Böylece çift ok işlevi, herhangi bir PureComponent bileşeninin her zaman yeniden oluşturulmasına neden olur.

Misal

Değişiklik işleyicisine sahip bir üst bileşene sahipsiniz:

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

ve şöyle bir render ile:

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

daha sonra bir girişte veya tıklamada kullanılır. Ve hepsi işe yarıyor ve çok hoş görünüyor. ANCAK, ebeveynin yeniden ilişkilendirilmesine neden olacak herhangi bir değişikliğin (tamamen ilgisiz bir durum değişikliği gibi) PureComponent olsalar bile MyTask'ınızın TÜMÜNÜ de yeniden oluşturacağı anlamına gelir.

Bu, 'en' oku ve onu besleyeceğiniz veya özel bir shouldUpdate işlevi yazmak veya adlandırılmış işlevler yazmak (ve bunu manuel olarak bağlamak ...) gibi temel bilgilere geri dönmek gibi birçok yolu hafifletebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.