Javascript'te kendi kendini yürütme işlevinin amacı nedir?


427

Javascript'te, bunu ne zaman kullanmak istersiniz:

(function(){
    //Bunch of code...
})();

bunun üzerinde:

//Bunch of code...

3
Ayrıca ( teknik ) bir açıklamaya ve buraya bir göz atın . Sözdizimi için, parantezin neden gerekli olduğunu ve nereye gitmeleri gerektiğini görün .
Bergi


Neden noktalı virgülden hemen önce son iki parantez var?
johnny

3
@ son iki parantezden önceki bölüm (anonim) bir işlev bildirir. Bu iki parantez işlevi çağırır.
Ej.

7
"Hemen Başlatılan İşlev İfadesi" veya IIFE bunun için daha iyi bir addır .
Flimm

Yanıtlar:


404

Her şey değişken kapsam belirleme ile ilgilidir. Otomatik yürütme işlevinde bildirilen değişkenler varsayılan olarak yalnızca otomatik yürütme işlevi içindeki kod için kullanılabilir. Bu, değişkenlerin diğer JavaScript kod bloklarında nasıl adlandırıldığından endişe etmeden kodun yazılmasına izin verir.

Örneğin, Alexander tarafından yapılan bir yorumda belirtildiği gibi :

(function() {
  var foo = 3;
  console.log(foo);
})();

console.log(foo);

Bu, ilk günlüğe kaydeder 3ve ardından sonraki bir hata atmak console.logNEDENİYLE footanımlı değil.


7
Ve ayrıca bir sürü Netflix Mühendisleri de dahil olmak üzere birçok insanın yararı için: SADECE BİR FONKSİYON. Kendi başına bir kapanışın temsilcisi değildir. Bazen otomatik invokatörler düzgün şeyler yapmak için kapanışla ilgili senaryolarla birlikte kullanılırlar, ancak çöp toplanacak ve kapanmayan bir dilde olacak bir referansa tutunan bir şey görmezseniz, ucube KAPALI yapmak.
Erik Reppen

2
Yani bu, çoğunlukla kapama ile mi kullanılıyor?
Pir Abdul

@AlexanderBird, sen olmadan yapamaz pek doğru ... eğer varböyle: ...function(){ foo=3;}? Global değişken belirleyecekti, değil mi?
T.Todua

2
@AlexanderBird ama bu fonksiyonlar içindeki yerel değişkenlerde zaten oluyor: function(){ var foo = 3; alert(foo); }; alert(foo);Bu yüzden hala anlamıyorum
João Pimentel Ferreira

Ah, anladım, tüm bu 3 özelliği bir kerede elde edersiniz: 1) işlevi yalnızca bildirerek yürütürsünüz; 2) Herhangi bir işlev olarak değişken kapsamı yalnızca yereldir; ve 3) İşlev, ana kapsamı kirletmeyen anonim olabilir
João Pimentel Ferreira

94

Basit. Çok normal bir görünüm, neredeyse rahatlatıcı:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

Ancak, sayfama gelişmiş karakterleri temel düzey gösterimlerine çeviren gerçekten kullanışlı bir javascript kitaplığı eklersem ne olur?

Bir dakika ne?

Demek istediğim, birisi üzerinde aksanı olan bir karakter yazıyorsa, ama sadece programımda AZ 'İngilizce' karakterler istiyorum? Şey ... İspanyolca 'ñ' ve Fransızca 'é' karakterleri 'n' ve 'e' nin temel karakterlerine çevrilebilir.

Bu yüzden iyi biri, siteme ekleyebileceğim kapsamlı bir karakter dönüştürücü yazdı ... Ben de dahil ediyorum.

Bir sorun: İçinde benim işlevimle aynı 'isim' adlı bir işlevi var.

Çarpışma denen şey budur. Aynı bildirilen iki işlevi var kapsamı aynı adla. Bundan kaçınmak istiyoruz.

Bu yüzden kodumuzu bir şekilde kapsamamız gerekiyor.

Javascript kod kapsamı için tek yolu bir işlevde sarmak için:

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Bu bizim sorunumuzu çözebilir. Artık her şey kapalı ve sadece açılış ve kapanış parantezlerimizden erişilebilir.

Bir fonksiyonda bir fonksiyonumuz var ... bakmak garip ama tamamen yasal.

Sadece bir sorun. Kodumuz çalışmıyor. UserName değişkenimiz asla konsola yansıtılmaz!

Mevcut kod bloğumuzdan sonra fonksiyonumuza bir çağrı ekleyerek bu sorunu çözebiliriz ...

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

Ya da önce!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

İkincil bir endişe: 'Ana' isminin henüz kullanılma ihtimali nedir? ... çok, çok ince.

DAHA FAZLA kapsam belirlemeye ihtiyacımız var. Ve ana () işlevimizi otomatik olarak yürütmenin bir yolu.

Şimdi otomatik yürütme işlevlerine (veya kendi kendine, kendi kendine çalışan, her neyse) geliyoruz.

((){})();

Sözdizimi günah olarak gariptir. Ancak işe yarıyor.

Bir işlev tanımını parantez içine aldığınızda ve bir parametre listesi (başka bir küme veya parantez!) Eklediğinizde, işlev çağrısı olarak işlev görür .

Bu yüzden, kendi kendini yürüten sözdizimi ile kodumuza tekrar bakalım:

(function main() {
  var userName = "Sean";

    console.log(name());

    function name() {
      return userName;
    }
  }
)();

Yani, okuduğunuz çoğu öğreticide, artık 'anonim kendi kendini yürütme' terimi veya benzer bir şeyle bombardıman olacaksınız.

Yıllarca süren mesleki gelişimden sonra, hata ayıklama amacıyla yazdığınız her işlevi adlandırmanızı şiddetle tavsiye ediyorum .

Bir şeyler ters gittiğinde (ve olacak), tarayıcınızdaki geri izlemeyi kontrol edeceksiniz. Öyle her zaman yığın izleme girişleri ada sahip olduklarında kodunuzu sorunları daraltmak için daha kolay!

Çok uzun soluklu ve umarım yardımcı olur!


2
Teşekkür ederim :) İnternette değişken gizlilik açısından normal fonksiyonlarla ilgili olarak IIFE'nin avantajlarını anlamaya çalışıyordum ve cevabınız en iyisi. Herkes en iyi avantajlardan birinin, normal bir fonksiyon size tam olarak aynı şeyi verdiğinde IIFE içindeki değişkenlerin ve fonksiyonların 'nihayet' özel olduğunu söyler. Sonunda açıklama sürecinden geçtim. Sonuçta IIFE sadece bir işlev ama şimdi neden kullanacağımı anlıyorum.
viery365

Bunu çok iyi açıklamak için zaman ayırdığınız için teşekkür ederiz.
MSC

Güzel cevap. Son noktanızla ilgili bir sorum var - Tüm işlevlerin adlandırılmasını önerdiğinizde, bunu kendi kendine yürüten işlevlerle yapmanın bir yolu olduğunu mu söylüyorsunuz, ya da herkesin bu işlevi çağırmasını öneriyor musunuz? EDIT Oh, anlıyorum. Bu zaten adlandırılmış. Duh. Adlandırılmış bir kendi kendini yürütme işlevini kullanımınızı haklı gösterdiğinizi belirtmek isteyebilir.
FireSBurnsmuP

Peki arkadaşım, bu aradığım cevap:)
João Pimentel Ferreira

Aradığım cevap buydu, kabul edilen işaret değil. Değişken adı çarpışmasıyla ilgili değil, işlev adı çarpışmasıyla ilgili olduğunu söylerken haklı mıyım ? Normal hayat dışı işlevlerde bile değişkenler yerel olarak kapsamlıdır ve küresel değişkenlerle çarpışmaz, değil mi?
Kumar Manish

32

Kendi kendine çağırma (otomatik çağırma olarak da bilinir), bir işlev tanımının hemen ardından yürütülür. Bu temel bir modeldir ve diğer birçok JavaScript geliştirme modelinin temelini oluşturur.

Ben büyük bir hayranıyım :) çünkü:

  • Kodu minimumda tutar
  • Davranışın sunumdan ayrılmasını zorunlu kılar
  • Çakışma adlarını önleyen bir kapatma sağlar

Muazzam bir şekilde - (Neden iyi olduğunu söylemelisin?)

  • Bir işlevi bir kerede tanımlamak ve uygulamakla ilgilidir.
  • Kendi kendini yürütme işlevinin bir değer döndürmesini ve işlevi bir parametre olarak başka bir işleve geçirmesini sağlayabilirsiniz.
  • Kapsülleme için iyidir.
  • Blok kepçesi için de iyidir.
  • Evet, tüm .js dosyalarınızı kendi kendini yürütme işlevine ekleyebilir ve global ad alanı kirliliğini önleyebilirsiniz. ;)

Daha burada .


43
Nokta 1. Nasıl? Nokta 2. Bu tamamen farklı bir en iyi uygulamadan. Nokta 3. Hangi işlev geçerli değildir? 4,5,6,7. Uygunluk? 8. Şey, sanırım 1/8 kötü değil.
Erik Reppen

2
Yedi yıl geç, ancak 1. nokta için kodu hiç azaltmaz, aslında işlevi oluştururken en az iki satır kod ekler.
YungGun

1
Buradaki tek nokta, "Çatışmaların isimlendirilmesini engelleyen bir kapatma sağlar", diğer her nokta bu ya da yanlışın yeniden yazılmasıdır. belki cevabınızı basitleştirebilirsiniz?
pcarvalho

19

Ad alanlarının. JavaScript'in kapsamları işlev düzeyindedir.


7
downvotes hala kullandığım çünkü geliyor Namespacing ait instad kapsama alınması ; bu bir tanım meselesidir - bakınız örn. Wikipedia : Bilgisayar bilimindeki bir ad alanı (bazen ad kapsamı da denir), benzersiz tanımlayıcıların veya sembollerin (yani adların) mantıksal bir gruplamasını tutmak için oluşturulan soyut bir kap veya ortamdır. ve Bir ad alanı tanımlayıcısı bir ad için bağlam (bilgisayar biliminde kapsam) sağlayabilir ve terimler bazen birbirinin yerine kullanılabilir.
Christoph

5
Javascript'in işlev düzeyi kapsamları, değişken adlarının yaşadığı alanı , bir ad alanını sağlar ; bir ad alanı tanımlayıcısı ile ilişkili olmayan anonim bir önemsiz olduğunu ...
Christoph

12

Cevapların hiçbirinin ima edilen küresellerden bahsettiğine inanamıyorum.

(function(){})()Yapı bkz bana büyük endişe olduğunu ima globaller, karşı koruma sağlamaz http://yuiblog.com/blog/2006/06/01/global-domination/

Temel olarak işlev bloğu, tanımladığınız tüm bağımlı "global değişkenler" in programınızla sınırlı olmasını sağlar, sizi örtülü globalleri tanımlamaya karşı korumaz. JSHint veya benzerleri, bu davranışa karşı nasıl savunma yapılacağına dair öneriler sağlayabilir.

Daha özlü var App = {}sözdizimi benzer bir koruma düzeyi sağlar ve 'genel' sayfalardayken işlev bloğuna sarılabilir. ( bu yapıyı kullanan kütüphanelerin gerçek dünya örnekleri için Ember.js veya SproutCore'a bakın )

Mülklere gelince private, kamusal bir çerçeve veya kütüphane oluşturmadığınız sürece biraz abartılıyorlar, ancak bunları uygulamanız gerekiyorsa, Douglas Crockford'un bazı iyi fikirleri var.


8
Sıkı mod, zımni globallere karşı koruma sağlar. Bu bir otomatik invoker ile birlikte sizi ele geçirir. Hooplah'ı özel mülkler üzerinde hiç anlamadım. Bir func yapıcısının içinde değişkenler bildirir. Bitti. 'Yeni' anahtar kelimeyi kullanmayı unutma düşüncesi sizi gece ayağa kaldırırsa, bir fabrika işlevi yazın. Yine bitti.
Erik Reppen

8

Bütün cevapları okudum, burada çok önemli bir şey eksik , Öpeceğim. Neden kendi kendini yürüten anonim işlevlere ihtiyacım veya daha iyi " Hemen çağırılan işlev ifadesi (IIFE) " dedi 2 ana nedeni vardır :

  1. Daha iyi ad alanı yönetimi (Ad Alanı Kirliliğini Önleme -> JS Modülü)
  2. Kapanışlar (OOP'tan bilindiği üzere Özel Sınıf Üyelerini Simüle Etme)

İlki çok iyi açıklandı. İkincisi için lütfen aşağıdaki örneği inceleyin:

var MyClosureObject = (function (){
  var MyName = 'Michael Jackson RIP';
  return {
    getMyName: function () { return MyName;},
    setMyName: function (name) { MyName = name}
  }
}());

Dikkat 1: Daha MyClosureObjectfazla bu işlevi çağırmanın sonucu olarak bir işlev atamıyoruz . ()Son satırdan haberdar olun .

Dikkat 2: Javascript'teki işlevler hakkında ek olarak bilmeniz gerekenler, iç işlevlerin, içinde tanımlandıkları işlevlerin parametrelerine ve değişkenlerine erişebilmesidir .

Bazı deneyleri deneyelim:

Ben alabilirim MyNamekullanarak getMyNameve çalışır:

 console.log(MyClosureObject.getMyName()); 
 // Michael Jackson RIP

Aşağıdaki ustaca yaklaşım işe yaramaz:

console.log(MyClosureObject.MyName); 
// undefined

Ama başka bir isim ayarlayabilir ve beklenen sonucu elde edebilirim:

MyClosureObject.setMyName('George Michael RIP');
console.log(MyClosureObject.getMyName()); 
// George Michael RIP

Düzenleme: Yukarıdaki örnekte önek MyClosureObjectolmadan kullanılmak üzere tasarlanmıştır new, bu nedenle konvansiyon ile büyük harfle yazılmamalıdır.


7

Bir parametre var mı ve "Kod grubu" bir işlev döndürüyor mu?

var a = function(x) { return function() { document.write(x); } }(something);

Kapanış. somethingAtanan işlev tarafından kullanılan değeri a. something(döngü için) bazı değişken değerlere sahip olabilir ve a'nın her yeni işlevi olduğunda.


+ 1; Yine var x = something;de dış fonksiyonda xparametre olarak açık bir şekilde tercih ederim : imo bu şekilde daha okunabilir ...
Christoph

@Christoph: İşlev oluşturulduktan sonra "bir şeyin" değeri değişirse, yaratıldığı andaki değeri değil, yeni değeri kullanır.
stesch

@stesch: bunu nereden aldın? Bildiğim kadarıyla durum böyle değil; JS'de gerçek referanslar almanın tek yolu arguments nesnesini kullanmaktır, ancak bu tüm tarayıcılarda çalışmaz
Christoph

@Christoph: "JavaScript: İyi Parçalar", Douglas Crockford (O'Reilly)
stesch

@stesch: tanımladığınız şekilde çalışmaz: Değişkeni düşürürseniz xve doğrudan sözcüksel kapsama bağlı kalırsanız yeni değer kullanılır , yani document.write(something)...
Christoph

6

Belki kapsam izolasyonu. Böylece işlev bildirimindeki değişkenler dış ad alanını kirletmez.

Tabii ki, orada JS uygulamalarının yarısında, yine de yapacaklar.


4
Bunlar ne gibi uygulamalar olurdu?
Matthew Crumley

1
Katı modda yazılmamış ve küresel olmasına neden olan varyant içeren ve üstü kapalı herhangi bir uygulama.
Erik Reppen

5

Kendini çağıran anonim bir işlevin nasıl yararlı olabileceğine dair sağlam bir örnek.

for( var i = 0; i < 10; i++ ) {
  setTimeout(function(){
    console.log(i)
  })
}

Çıktı: 10, 10, 10, 10, 10...

for( var i = 0; i < 10; i++ ) {
  (function(num){
    setTimeout(function(){
      console.log(num)
    })
  })(i)
}

Çıktı: 0, 1, 2, 3, 4...


ilk kod seti için neler olduğunu biraz daha açıklayabilir misiniz
radio_head

İle letyerine varbirinci durumda düzelecek.
Vitaly Zdanevich

3

Bir fark, işlevde bildirdiğiniz değişkenlerin yerel olmasıdır, bu nedenle işlevden çıktığınızda ve diğer koddaki diğer değişkenlerle çakışmadığınızda giderler.


1

Javascript'teki işlevler birinci sınıf nesne olduğundan, onu bu şekilde tanımlayarak, C ++ veya C # gibi bir "sınıfı" etkili bir şekilde tanımlar.

Bu işlev yerel değişkenleri tanımlayabilir ve içinde işlevlere sahip olabilir. Dahili işlevler (etkin örnek yöntemleri) yerel değişkenlere (etkin örnek değişkenleri) erişebilir, ancak komut dosyasının geri kalanından yalıtılır.


1

Javascript kendinden çağrılan fonksiyonu:

Kendini çağıran bir ifade çağrılmadan otomatik olarak çağrılır (başlatılır). Kendini çağıran bir ifade, oluşturulduktan hemen sonra çağrılır. Bu, temel olarak, adlandırma çatışmasını önlemek ve kapsüllemeyi sağlamak için kullanılır. Değişkenlere veya bildirilen nesnelere bu işlevin dışında erişilemez. Minimizasyon (filename.min) problemlerinden kaçınmak için her zaman kendi kendine yürütülen işlevi kullanın.


1

Kendi kendini yürütme işlevi, bir Değişkenin kapsamını yönetmek için kullanılır.

Bir değişkenin kapsamı, programınızın tanımlandığı bölgesidir.

Küresel bir değişkenin küresel kapsamı vardır; JavaScript kodunuzun her yerinde tanımlanır ve komut dosyasındaki herhangi bir yerden, hatta işlevlerinizden erişilebilir. Öte yandan, bir işlev içinde bildirilen değişkenler yalnızca işlevin gövdesi içinde tanımlanır. Yerel değişkenlerdir, yerel kapsama sahiptirler ve yalnızca bu işlev içinde erişilebilirler. İşlev parametreleri de yerel değişkenler olarak sayılır ve yalnızca işlevin gövdesi içinde tanımlanır.

Aşağıda gösterildiği gibi, işleviniz içindeki global değişken değişkene erişebilir ve ayrıca bir işlev gövdesi içinde yerel bir değişkenin aynı ada sahip bir global değişkene göre öncelikli olduğunu unutmayın.

var globalvar = "globalvar"; // this var can be accessed anywhere within the script

function scope() {
    alert(globalvar);
    localvar = "localvar" //can only be accessed within the function scope
}

scope(); 

Temel olarak kendi kendini yürüten bir işlev, değişkenlerin diğer javascript kod bloklarında nasıl adlandırıldığından endişe etmeden kodun yazılmasına izin verir.


1
(function(){
    var foo = {
        name: 'bob'
    };
    console.log(foo.name); // bob
})();
console.log(foo.name); // Reference error

Aslında, yukarıdaki fonksiyon isimsiz fonksiyon ifadesi olarak değerlendirilecektir.

Bir işlevi yakın ve açık parantez ile sarmanın temel amacı, küresel alanı kirletmekten kaçınmaktır.

İşlev ifadesinin içindeki değişkenler ve işlevler özel hale geldi (yani) işlevin dışında kullanılamayacaklar.


1

Kısa cevap: Küresel (veya daha yüksek) kapsamın kirlenmesini önlemek.

IIFE (Hemen Çağırılan İşlev İfadeleri), komut dosyalarını eklenti, eklenti, kullanıcı komut dosyası veya diğer komut dosyalarıyla çalışması beklenen komut dosyaları olarak yazmak için en iyi uygulamadır . Bu, tanımladığınız herhangi bir değişkenin diğer komut dosyaları üzerinde istenmeyen etkiler vermemesini sağlar.

Bu, IIFE ifadesini yazmanın diğer yoludur. Şahsen bu yöntemi tercih ederim:

void function() {
  console.log('boo!');
  // expected output: "boo!"
}();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

Yukarıdaki örnekten, IIFE'nin verimliliği ve performansı da etkileyebileceği çok açıktır, çünkü sadece bir kez çalıştırılması beklenen fonksiyon bir kez yürütülür ve daha sonra iyilik için boşluğa atılır . Bu, işlev veya yöntem bildiriminin bellekte kalmadığı anlamına gelir.


Güzel, daha voidönce bu kullanımı görmemiştim . Bunu sevdim.
Ej.

1

Önce MDN IIFE'yi ziyaret etmelisiniz , Şimdi bununla ilgili bazı noktalar

  • bu hemen çağırılan işlev ifadesidir . Javascript dosyanız HTML'den çağrıldığında bu işlev hemen çağrılır.
  • Bu, küresel kapsamı kirletmenin yanı sıra IIFE deyimindeki değişkenlere erişmeyi önler.

0

Görünüşe göre bu soru tamamen cevaplandı, ama yine de girdimi göndereceğim.

Kendini yürütme işlevlerini ne zaman kullanmayı sevdiğimi biliyorum.

var myObject = {
    childObject: new function(){
        // bunch of code
    },
    objVar1: <value>,
    objVar2: <value>
}

İşlev, childObjects özniteliklerini ve daha sık kullanılan değişkenlerin ayarlanması veya matematik denklemlerinin yürütülmesi; Ah! veya hata kontrolü. iç içe nesne örnekleme sözdizimi ile sınırlı olmanın aksine ...

object: {
    childObject: {
        childObject: {<value>, <value>, <value>}
    }, 
    objVar1: <value>,
    objVar2: <value>
}

Genel olarak kodlamanın, aynı şeyleri yapmak için çok belirsiz yöntemleri vardır, "Neden rahatsız oluyorsunuz?" Ancak artık yalnızca temel / çekirdek prensiplere güvenemeyeceğiniz yeni durumlar ortaya çıkıyor.


0

Basit sorunuz varsa: "Javascript'te, bunu ne zaman kullanmak istersiniz: ..."

@Ken_browning ve @ sean_holding'in cevaplarını seviyorum, ancak burada bahsetmediğim başka bir kullanım örneği:

let red_tree = new Node(10);

(async function () {
    for (let i = 0; i < 1000; i++) {
        await red_tree.insert(i);
    }
})();

console.log('----->red_tree.printInOrder():', red_tree.printInOrder());

Burada Node.insert bazı eşzamansız eylemlerdir.

Ben sadece benim işlev beyanında async anahtar sözcüğü olmadan bekleyemezsiniz, ve daha sonra kullanmak için adlandırılmış bir işleve gerek yok, ama bu arama eklemek için beklemek gerekir veya ben (kim bilir?) Bazı daha zengin özelliklere ihtiyacım var .


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.