Neden adlandırılmış işlev ifadeleri kullanılıyor?


94

JavaScript'te işlev ifadesi yapmanın iki farklı yolu var:

Adlandırılmış işlev ifadesi (NFE) :

var boo = function boo () {
  alert(1);
};

Anonim işlev ifadesi :

var boo = function () {
  alert(1);
};

Ve her ikisi ile çağrılabilir boo();. Anonim işlevleri neden / ne zaman kullanmam gerektiğini ve Adlandırılmış İşlev İfadelerini ne zaman kullanmam gerektiğini gerçekten anlayamıyorum. Aralarında ne fark var?


Yanıtlar:


87

Anonim işlev ifadesi durumunda, işlev anonimdir  - tam anlamıyla adı yoktur. Ona atadığınız değişkenin bir adı var, ancak işlev yok. (Güncelleme: Bu ES5 aracılığıyla doğruydu. ES2015'ten itibaren [ES6 olarak da bilinir], genellikle anonim bir ifadeyle oluşturulan bir işlev gerçek bir ad alır [ancak otomatik bir tanımlayıcı değil], okumaya devam edin ...)

İsimler kullanışlıdır. İsimler yığın izlerinde, çağrı yığınlarında, kesme noktası listelerinde vb. Görülebilir. İsimler bir İyi Şeydir ™.

(Eskiden IE'nin eski sürümlerinde [IE8 ve altı] adlandırılmış işlev ifadelerine dikkat etmeniz gerekirdi, çünkü bunlar yanlışlıkla iki tamamen farklı zamanda tamamen ayrı iki işlev nesnesi oluşturdular [blog makalemde daha fazlası Double take ]. Gerekirse IE8 [!!] desteği, muhtemelen anonim işlev ifadelerine veya işlev bildirimlerine bağlı kalmak en iyisidir , ancak adlandırılmış işlev ifadelerinden kaçının.)

Adlandırılmış bir işlev ifadesiyle ilgili önemli bir şey, işlev gövdesi içindeki işlev için bu adla bir kapsam içi tanımlayıcı oluşturmasıdır:

var x = function example() {
    console.log(typeof example); // "function"
};
x();
console.log(typeof example);     // "undefined"

Ancak ES2015'ten itibaren, birçok "anonim" işlev ifadesi adlarla işlevler oluşturur ve bu, çeşitli modern JavaScript motorlarının adların bağlamdan çıkarılması konusunda oldukça akıllı olmasından önce yapılmıştır. ES2015'te, anonim işlev ifadeniz adıyla bir işlevle sonuçlanır boo. Bununla birlikte, ES2015 + anlambilimiyle bile, otomatik tanımlayıcı oluşturulmaz:

var obj = {
    x: function() {
       console.log(typeof x);   // "undefined"
       console.log(obj.x.name); // "x"
    },
    y: function y() {
       console.log(typeof y);   // "function"
       console.log(obj.y.name); // "y"
    }
};
obj.x();
obj.y();

Fonksiyonun adının atanması , spesifikasyondaki çeşitli işlemlerde kullanılan SetFunctionName özet işlemi ile yapılır .

Kısa sürüm, temelde, bir atama veya başlatma gibi bir şeyin sağ tarafında anonim bir işlev ifadesi göründüğünde, örneğin:

var boo = function() { /*...*/ };

(ya da olabilir letya da constyerine var) , ya da

var obj = {
    boo: function() { /*...*/ }
};

veya

doSomething({
    boo: function() { /*...*/ }
});

(bu son ikisi gerçekten aynı şeydir) , ortaya çıkan işlevin bir adı olacaktır ( booörneklerde).

Önemli ve kasıtlı bir istisna vardır: Var olan bir nesne üzerindeki bir özelliğe atama:

obj.boo = function() { /*...*/ }; // <== Does not get a name

Bunun nedeni, yeni özellik eklenme sürecinden geçerken ortaya çıkan bilgi sızıntısı endişeleriydi; Burada başka bir soruya verdiğim cevabın ayrıntıları .


1
NFE'lerin kullanılmasının hala somut avantajlar sağladığı en az iki yer olduğunu belirtmekte fayda var: birincisi, newoperatör aracılığıyla yapıcı olarak kullanılması amaçlanan işlevler için (bu tür tüm işlevlerin adlarının verilmesi, .constructorözelliği hata ayıklama sırasında ne olduğunu anlamak için daha kullanışlı hale getirir. bazı nesneler) 'nin bir örneğidir ve işlev değişmezleri için, önce bir özelliğe veya değişkene atanmadan doğrudan bir işleve aktarılır (örn. setTimeout(function () {/*do stuff*/});). Chrome bile, bunları (anonymous function)adlandırarak yardımcı olmadığınız sürece bunları gösterir.
Mark Amery

4
@MarkAmery: "Bu hala doğru mu? Ben ... Bu kurallar için CTRL-F'yi denedim ve bulamadım" Ah evet. :-) Bir kurallar setini tanımlayan tek bir yerde olmak yerine, spesifikasyon boyunca dağılmıştır, sadece "setFunctionName" için arama yapın. Yukarıya küçük bir bağlantı alt kümesi ekledim, ancak şu anda ~ 29 farklı yerde görünüyor. Örneğinizin setTimeoutismini beyan edilen resmi argümandan almasa setTimeout, sadece biraz şaşırırdım . :-) Ama evet, NFE'ler, onlardan bir karma oluşturan eski tarayıcılarla uğraşmayacağınızı biliyorsanız kesinlikle yararlıdır.
TJ Crowder

25

Adlandırma işlevleri, kendilerine referans vermeleri gerektiğinde yararlıdır (örneğin, özyinelemeli çağrılar için). Başka bir fonksiyona doğrudan bağımsız değişken olarak bir hazır işlev ifadesi geçiyoruz Nitekim, bu işlev ifadesi olamaz Adını sürece doğrudan ES5 katı modda kendisini referans.

Örneğin, şu kodu göz önünde bulundurun:

setTimeout(function sayMoo() {
    alert('MOO');
    setTimeout(sayMoo, 1000);
}, 1000);

Aktarılan işlev ifadesi setTimeoutanonim olsaydı, bu kodu bu kadar temiz yazmak imkansız olurdu ; setTimeoutçağrıdan önce onu bir değişkene atamamız gerekir . Bu şekilde, adlandırılmış bir işlev ifadesi ile biraz daha kısa ve daha derli toplu olur.

Tarihsel olarak, anonim bir işlev ifadesi kullanarak bile böyle bir kod yazmak, arguments.callee...

setTimeout(function () {
    alert('MOO');
    setTimeout(arguments.callee, 1000);
}, 1000);

... ancak arguments.calleekullanımdan kaldırıldı ve ES5 katı modunda tamamen yasaklandı. Bu nedenle MDN şunları tavsiye eder:

Kullanmaktan kaçının arguments.callee()biri tarafından verilmesi işlev ifadeleri bir isim ya da bir işlev kendisini çağırmalıdır nerede bir işlevi bildirimi kullanın.

(vurgu benim)


3

Bir işlev, İşlev İfadesi olarak belirtilirse, bir ad verilebilir.

Yalnızca işlevin içinde mevcut olacaktır (IE8- hariç).

var f = function sayHi(name) {
  alert( sayHi ); // Inside the function you can see the function code
};

alert( sayHi ); // (Error: undefined variable 'sayHi')

Bu ad, başka bir değişkene yazılsa bile güvenilir bir özyinelemeli işlev çağrısı için tasarlanmıştır.

Ek olarak, NFE (İsimli Fonksiyon İfadesi) adı Object.defineProperty(...)aşağıdaki yöntemle üzerine yazılabilir :

var test = function sayHi(name) {
  Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
  alert( test.name ); // foo
};

test();

Not: İşlev Bildirimi ile bu yapılamaz. Bu "özel" dahili işlev adı yalnızca İşlev İfadesi sözdiziminde belirtilir.


2

Her zaman adlandırılmış işlev ifadeleri kullanmalısınız , bu yüzden:

  1. Özyinelemeye ihtiyaç duyduğunuzda bu işlevin adını kullanabilirsiniz.

  2. Anonim işlevler, sorunlara neden olan işlevin adını göremediğiniz için hata ayıklama sırasında yardımcı olmaz.

  3. Bir işlevi adlandırmadığınızda, daha sonra ne yaptığını anlamak daha zordur. Ona bir isim vermek, anlamayı kolaylaştırır.

var foo = function bar() {
    //some code...
};

foo();
bar(); // Error!

Burada, örneğin, ad çubuğu bir işlev ifadesi içinde kullanıldığından, dış kapsamda bildirilmez. Adlandırılmış fonksiyon ifadeleri ile, fonksiyon ifadesinin adı kendi kapsamı içinde yer alır.


1

Gibi kullanımdan kaldırılmış özelliklere güvenmek zorunda kalmadan söz konusu işleve başvurabilmek istediğinizde adlandırılmış işlev ifadelerini kullanmak daha iyidir arguments.callee.


3
Bu bir cevaptan çok bir yorumdur. Belki detaylandırma faydalı olabilir
vsync
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.