'Ok İşlevleri' ve 'İşlevler' eşdeğer / değiştirilebilir mi?


520

ES2015'teki ok işlevleri daha özlü bir sözdizimi sağlar.

  • Tüm işlev bildirimlerimi / ifadelerimi şimdi ok işlevleriyle değiştirebilir miyim?
  • Neye dikkat etmeliyim?

Örnekler:

Yapıcı işlevi

function User(name) {
  this.name = name;
}

// vs

const User = name => {
  this.name = name;
};

Prototip yöntemleri

User.prototype.getName = function() {
  return this.name;
};

// vs

User.prototype.getName = () => this.name;

Nesne (gerçek) yöntemleri

const obj = {
  getName: function() {
    // ...
  }
};

// vs

const obj = {
  getName: () => {
    // ...
  }
};

Callbacks

setTimeout(function() {
  // ...
}, 500);

// vs

setTimeout(() => {
  // ...
}, 500);

Değişken fonksiyonlar

function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// vs
const sum = (...args) => {
  // ...
};

5
Ok fonksiyonları ile ilgili benzer sorular, ES2015'in daha popüler hale gelmesiyle giderek daha fazla ortaya çıktı. Bu konuda iyi bir kanonik soru / cevap var gibi hissetmedim, bu yüzden bunu yarattım. Zaten iyi bir tane olduğunu düşünüyorsanız, lütfen bana bildirin, bunu kopya olarak veya sileceğim. Örnekleri geliştirmekten veya yenilerini eklemekten çekinmeyin.
Felix Kling

2
JavaScript ecma6 normal işlevini ok işlevine değiştirmeye ne dersiniz ? Elbette, normal bir soru hiçbir zaman özel olarak kanonik olarak yazılan soru kadar iyi ve genel olamaz.
Bergi

Bu Plnkr örneğine bakın. Düğme her çağrıldığında değişken thisçok fazla timesCalledartar. Ki bu benim kişisel soruma cevap veriyor: .click( () => { } )ve .click(function() { }) her ikisi de Plnkr'daki Guid sayısından görebileceğiniz gibi bir döngüde kullanıldığında aynı sayıda fonksiyon yaratıyor.
abbaf33f

Yanıtlar:


750

tl; dr: Hayır! Ok işlevleri ve işlev bildirimleri / ifadeleri eşdeğer değildir ve körü körüne değiştirilemez.
Eğer yok değiştirmek istediğiniz işlevi ise değil kullanmak this, argumentsve ile çağrılmaz new, o zaman evet.


Sık sık olduğu gibi: duruma göre değişir . Ok işlevleri, işlev bildirimleri / ifadelerinden farklı davranışlara sahiptir, bu yüzden önce farklılıklara bir göz atalım:

1. Sözcüksel thisvearguments

Ok işlevlerinin kendilerine ait thisveya argumentsbağları yoktur. Bunun yerine, bu tanımlayıcılar diğer değişkenler gibi sözcüksel kapsamda çözümlenir. Bu demektir ki, bir ok işlevi içinde, bu thisve argumentsdeğerlerine bakın thisve argumentsok fonksiyonu olan bir ortamda tanımlanan (yani, "dış" ok fonksiyonu) 'de:

// Example using a function expression
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: function() {
      console.log('Inside `bar`:', this.foo);
    },
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

// Example using a arrow function
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: () => console.log('Inside `bar`:', this.foo),
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

İşlev ifadesi durumunda, thisiçinde oluşturulan nesneyi ifade eder createObject. Ok fonksiyonu durumunda, thisanlamına gelir thisve createObjectkendi başına.

Bu this, geçerli ortama erişmeniz gerektiğinde ok işlevlerini kullanışlı hale getirir :

// currently common pattern
var that = this;
getData(function(data) {
  that.data = data;
});

// better alternative with arrow functions
getData(data => {
  this.data = data;
});

Bunun, bir ok işlevinin veya ile ayarlanmasının mümkün olmadığı anlamına geldiğini de unutmayın .this.bind.call

Çok aşina değilseniz thisokumayı düşünün

2. Ok fonksiyonları ile çağrılamaz new

Olan işlevler arasında ES2015 farklılaşacaktır çağrı vardır edebilmek ve işlevleri inşa edebilmek. Bir işlev inşa edilebilirse new, yani ile çağırılabilir new User(). Bir işlev çağırılabilirse, çağrılmadan çağrılabilir new(yani normal işlev çağrısı).

İşlev bildirimleri / ifadeleri ile oluşturulan işlevler hem oluşturulabilir hem de çağrılabilir.
Ok işlevleri (ve yöntemleri) yalnızca çağrılabilir. classyapıcılar sadece inşa edilebilir.

Çağrılamaz bir işlevi çağırmaya veya yapılandırılamayan bir işlev oluşturmaya çalışıyorsanız, bir çalışma zamanı hatası alırsınız.


Bunu bilerek, aşağıdakileri söyleyebiliriz.

değiştirilebilir:

  • thisVeya kullanmayan işlevler arguments.
  • Birlikte kullanılan işlevler .bind(this)

Değil değiştirilebilir:

  • Yapıcı fonksiyonları
  • Bir prototipe eklenen işlev / yöntemler (genellikle kullandıkları için this)
  • Variadic fonksiyonlar (kullanıyorlarsa arguments(aşağıya bakınız))

Örneklerinizi kullanarak buna daha yakından bakalım:

Yapıcı işlevi

Bu işe yaramaz çünkü ok fonksiyonları ile çağrılamaz new. Bir işlev bildirimi / ifadesi veya kullanımı kullanmaya devam edin class.

Prototip yöntemleri

Büyük olasılıkla hayır, çünkü prototip yöntemleri genellikle thisörneğe erişmek için kullanılır . Kullanmazlarsa this, değiştirebilirsiniz. Bununla birlikte, öncelikle özlü sözdizimini önemsiyorsanız, özlü sözdizimi classile kullanın:

class User {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

Nesne yöntemleri

Benzer şekilde, bir nesne değişmezindeki yöntemler için. Yöntem nesnenin kendisine başvurmak istiyorsa this, işlev ifadelerini kullanmaya devam edin veya yeni yöntem sözdizimini kullanın:

const obj = {
  getName() {
    // ...
  },
};

Callbacks

Değişir. Dış kısmı taklit ediyorsanız thisveya kullanıyorsanız kesinlikle değiştirmelisiniz .bind(this):

// old
setTimeout(function() {
  // ...
}.bind(this), 500);

// new
setTimeout(() => {
  // ...
}, 500);

Ama: açıkça geri arama çağıran kod belirlerse thisözellikle jQuery ile, sık sık olay işleyicileri ile olduğu gibi, belirli bir değere ve geri arama kullanımları this(veya arguments) kullanarak, olamaz bir ok işlevini kullanın!

Değişken fonksiyonlar

Ok işlevlerinin kendilerine ait olmaması nedeniyle, argumentsbunları bir ok işleviyle değiştiremezsiniz. Bununla birlikte, ES2015, kullanım için bir alternatif sunar arguments: rest parametresi .

// old
function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// new
const sum = (...args) => {
  // ...
};

İlgili soru:

Diğer kaynaklar:


6
Muhtemelen, sözlüğün thisde etkilediğini superve hiç olmadığını belirtmek gerekir .prototype.
loganfsmyth

1
Sözdizimsel olarak değiştirilemez olduklarını belirtmek de iyi olur - bir ok işlevi ( AssignmentExpression), bir işlev ifadesinin ( PrimaryExpression) yapabileceği her yere bırakılamaz ve insanları oldukça sık açar (özellikle ayrıştırıldığından beri) büyük JS uygulamalarındaki hatalar).
JMM

@JMM: "İnsanları oldukça sık açar " somut bir örnek verebilir misiniz? Spesifikasyon üzerinde kayma, bir FE koyabileceğiniz ancak bir AF değil yerlerin yine de çalışma zamanı hatalarına neden olacağı görülüyor ...
Felix Kling

Tabii, bir işlev ifadesi ( () => {}()) gibi hemen bir ok işlevini çağırmaya çalışmak veya benzeri bir şey yapmak gibi şeyler demek istiyorum x || () => {}. Demek istediğim bu: çalışma zamanı (ayrıştırma) hataları. (Durum böyle olsa da, oldukça sık insanlar hatanın hatalı olduğunu düşünüyor.) Sadece ayrıştırıldığında veya yürütüldüğünde mutlaka hata yapmadığı için fark edilmeyecek mantık hatalarını kapatmaya mı çalışıyorsunuz? newbiri çalışma zamanı hatası değil mi?
JMM

Vahşi doğada ortaya çıkan bazı bağlantılar: substack / node-browserify # 1499 , babel / babel-eslint # 245 (bu async bir ok, ama aynı temel sorun olduğunu düşünüyorum) ve bir dizi sorun Şimdi bulmak zor olan Babel, ama burada bir T2847 var .
JMM

11

Ok fonksiyonları => şimdiye kadarki en iyi ES6 özelliği. Sürekli kullandığım ES6'ya muazzam derecede güçlü bir ektir.

Bekle, kodunuzun her yerinde ok işlevini kullanamazsınız, ok işlevlerinin kullanılamadığı durumlarda her durumda çalışmaz this. Şüphesiz, ok işlevi kod basitliği getiren harika bir ektir.

Ancak dinamik bir bağlam gerektiğinde bir ok işlevi kullanamazsınız: yöntemleri tanımlamak, yapıcılarla nesne oluşturmak, olayları işlerken bundan hedef almak.

Ok fonksiyonları KULLANILMAMALIDIR çünkü:

  1. Sahip değiller this

    this” Değerinin ne olması gerektiğini anlamak için “sözcüksel kapsam belirleme” yi kullanır . Basit bir sözcükle sözcüksel kapsam belirleme thisişlevi işlevin gövdesinden “ ” kullanır .

  2. Sahip değiller arguments

    Ok işlevlerinin bir argumentsnesnesi yoktur. Ancak aynı işlevsellik dinlenme parametreleri kullanılarak da elde edilebilir.

    let sum = (...args) => args.reduce((x, y) => x + y, 0) sum(3, 3, 1) // output - 7 '

  3. İle kullanılamazlar new

    Bir prototip özelliği olmadığı için ok işlevleri yorumlayıcı olamaz.

Ok işlevinin ne zaman kullanılması ve ne zaman kullanılmaması:

  1. Nesne hazır bilgisinde özellik olarak işlev eklemek için kullanmayın çünkü buna erişemeyiz.
  2. İşlev ifadeleri nesne yöntemleri için en iyisidir. Fonksiyonları gibi geri çağrılar veya yöntemler için en iyisidir Arrow map, reduceya forEach.
  3. Ada göre çağırdığınız işlevler için işlev bildirimlerini kullanın (kaldırıldıkları için).
  4. Geri aramalar için ok işlevlerini kullanın (çünkü bunlar ters olma eğilimindedir).

2
2. Onlar argüman yok, üzgünüm doğru değil, bir kullanmadan argüman olabilir ... operatör, belki senin argüman olarak dizi olmadığını söylemek istiyorum
Carmine Tambascia

@CarmineTambascia argumentsBuradaki ok işlevlerinde bulunmayan özel nesne hakkında bilgi edinin : developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
vichle

0

Ok işlevlerini ile kullanmak function.prototype.calliçin, nesne prototipinde yardımcı bir işlev yaptım:

  // Using
  // @func = function() {use this here} or This => {use This here}
  using(func) {
    return func.call(this, this);
  }

kullanım

  var obj = {f:3, a:2}
  .using(This => This.f + This.a) // 5

Düzenle

Bir yardımcıya ihtiyacınız yok. Şunları yapabilirsiniz:

var obj = {f:3, a:2}
(This => This.f + This.a).call(undefined, obj); // 5
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.