Bu JavaScript kalıbı nedir ve neden kullanılır?


100

THREE.js üzerinde çalışıyorum ve işlevlerin şu şekilde tanımlandığı bir model fark ettim:

var foo = ( function () {
    var bar = new Bar();

    return function ( ) {
        //actual logic using bar from above.
        //return result;
    };
}());

(Örnek burada raycast yöntemine bakın ).

Normal Böyle bir yöntemin varyasyon şu şekilde görünecektir:

var foo = function () {
    var bar = new Bar();

    //actual logic.
    //return result;
};

İlk versiyonu normal varyasyonla karşılaştırdığımızda, ilk versiyon farklı görünüyor:

  1. Kendi kendine çalışan bir işlevin sonucunu atar.
  2. Bu işlev içinde yerel bir değişkeni tanımlar.
  3. Yerel değişkeni kullanan mantığı içeren gerçek işlevi döndürür .

Dolayısıyla temel fark, ilk varyasyonda çubuğun yalnızca bir kez atanması, ikinci varyasyonun her çağrıldığında bu geçici değişkeni yaratmasıdır.

Bunun neden kullanıldığına dair en iyi tahminim, bar için örnek sayısını sınırlaması (yalnızca bir tane olacaktır) ve böylece bellek yönetimi ek yükünden tasarruf sağlamasıdır.

Sorularım:

  1. Bu varsayım doğru mu?
  2. Bu model için bir isim var mı?
  3. Bu neden kullanılıyor?

1
@ChrisHayes yeterince adil. Bunu THREE.js olarak etiketledim çünkü THREE.js katkıda bulunanların bunu yanıtlamak için en nitelikli olduğunu düşündüm ama evet, bu genel bir JS sorusu.
Patrick Klug

2
Sanırım buna kapanış deniyor. Onlar hakkında okuyabilirsiniz.
StackFlowed

1
Bir Bar'ın somutlaştırıldığı tek yer burasıysa, o zaman bir tekli modeldir.
Paul

8
Hafızayı kurtarmak için zorunlu değildir, ancak durumu çağrılarda koruyabilir
Juan Mendes

2
@wrongAnswer: tam olarak değil. burada anonim işlev (kapanış olacaktır) anında çalıştırılır.
njzk2

Yanıtlar:


100

Varsayımlarınız neredeyse doğru. Önce bunları gözden geçirelim.

  1. Kendi kendine çalışan bir işlevin dönüşünü atar

Buna Anında çağrılan işlev ifadesi veya IIFE denir

  1. Bu işlev içinde yerel bir değişkeni tanımlar

Bu, privateanahtar kelimeyi veya işlevi başka şekilde sağlamadığından JavaScript'te özel nesne alanlarına sahip olmanın bir yoludur .

  1. Yerel değişkeni kullanan mantığı içeren gerçek işlevi döndürür.

Yine ana nokta, bu yerel değişkenin özel olmasıdır .

Bu model için bir isim var mı?

AFAIK bu kalıbı Modül Modeli olarak adlandırabilirsiniz . Alıntı yapmak:

Modül kalıbı, "gizlilik", durum ve organizasyonu kapatmaları kullanarak kapsüller. Herkese açık ve özel yöntemlerin ve değişkenlerin bir karışımını sarmanın, parçaların küresel kapsama sızmasını ve yanlışlıkla başka bir geliştiricinin arayüzüyle çarpışmasını önleyen bir yol sağlar. Bu modelle, yalnızca genel bir API döndürülür ve kapanış içindeki diğer her şey gizli tutulur.

Bu iki örneği karşılaştırdığımızda, ilkinin neden kullanıldığına dair en iyi tahminlerim şunlar:

  1. Singleton tasarım modelini uygulamaktadır.
  2. Birinci örnek kullanılarak belirli bir tipteki bir nesnenin yaratılma şekli kontrol edilebilir. Bu noktayla yakın bir eşleşme Etkili Java'da açıklandığı gibi statik fabrika yöntemleri olabilir .
  3. Bu var verimli Aynı nesne devlete her zaman gerekiyorsa.

Ancak her seferinde vanilya nesnesine ihtiyacınız varsa, bu model muhtemelen herhangi bir değer katmayacaktır.


1
Addy Osmani'nin kitabındaki kalıbı doğru şekilde tanımlamak için +1. Adlandırmanızda haklısınız - bu aslında modül kalıbıdır - bu arada bunda açıklayıcı modül kalıbı.
Benjamin Gruenbaum

4
'Kullanıma hazır özel değişkenler yok' bölümü dışında cevabınıza katılıyorum. Tüm JS değişkenleri sözcüksel olarak "özel" değişkenlerden (örn. Java'da bulunduğu gibi) daha güçlü / genel bir mekanizma olan "kullanıma hazır" kapsamlıdır; bu nedenle JS, tüm değişkenleri işleme yönteminin özel bir durumu olarak "özel" değişkenleri destekler.
Warbo

Sanırım "özel değişkenler" derken, özel "nesne alanını" kastettiniz
Ian Ringrose


4
@LukaHorvat: Aslında javascript diğer dillerden daha "güçlü" değil (ifade edici terimi tercih ederim). Aslında, değişkenlerinizi korumanın tek yolu, değişkenlerin yeniden kullanım saçmalıklarından kaçınmak için onları işlev içine almaktır. Modül kalıbı, iyi bir javascript kodu yapmak için kesin bir gerekliliktir, ancak dilin bir özelliği değildir, dilin zayıf yönleri tarafından ısırılmamak için daha üzücü bir çözümdür.
Falanwe

11

Nesne başlatma maliyetlerini sınırlar ve ek olarak tüm işlev çağrılarının aynı nesneyi kullanmasını sağlar . Bu, örneğin, gelecekteki çağrılar kullanmak üzere durumun nesnede depolanmasına izin verir.

Bellek kullanımını sınırlaması mümkün olsa da, genellikle GC zaten kullanılmayan nesneleri toplayacaktır, bu nedenle bu modelin pek bir faydası olmayacaktır.

Bu kalıp, belirli bir kapanış biçimidir .


1
JS'de genellikle 'modül' olarak
yeniden sunulur

2
Ben buna "belirli bir kapatma şekli" demezdim. Kapanış kullanan bir kalıp . Modelin adı hala kapmak için.
Chris Hayes

4
Gerçekten bir isme ihtiyacı var mı? Her şeyin bir kalıp olması gerekiyor mu? "Modül modeli" varyantlarının sonsuz bir sınıflandırmasına gerçekten ihtiyacımız var mı? "Bir işlev döndüren bazı yerel değişkenlere sahip bir IIFE" olamaz mı?
Dagg Nabbit

3
@DaggNabbit Soru "bu kalıba ne denir" olduğunda? Evet, bir isme ya da sahip olmadığı konusunda ikna edici bir argümana ihtiyacı var. Ayrıca kalıpların bir nedeni vardır. Neden burada onlara karşı korktuğunuzu anlamıyorum.
Chris Hayes

4
@ChrisHayes eğer bir isme ihtiyaç duyuyorsa, neden bir isme sahip olmadığına dair bir argüman yapmanız gerekiyor? Bu hiç mantıklı değil. İhtiyaç varsa, sahip olmamalıdır. Kalıplarla ilgili bir sorunum yok, ancak her bir basit deyimi kalıp olarak sınıflandırmanın gerekli olduğunu düşünmüyorum. Bunu yapmak, sınırlı şekillerde düşünmeye yol açar ("Bu bir modül modeli mi? Modül modelini doğru kullanıyorum mu?" Vs. "Bir işlev döndüren bazı yerel değişkenlere sahip bir IIFE'ye sahibim, bu tasarım benim için çalışıyor mu?")
Dagg Nabbit

8

Bu kalıbın daha doğru bir isme sahip olup olmadığından emin değilim, ancak bu bana bir modül gibi görünüyor ve kullanılmasının nedeni hem kapsüllemek hem de durumu korumak.

Kapanış (bir işlev içindeki bir işlevle tanımlanır), iç işlevin dış işlev içindeki değişkenlere erişmesini sağlar.

Verdiğiniz örnekte, iç işlev, foodış işlevin çalıştırılmasıyla döndürülür (ve atanır ); bu tmpObject, kapanış içinde yaşamaya devam ettiği anlamına gelir ve iç işleve birden çok çağrı, öğesinin foo()aynı örneğinde çalışır tmpObject.


5

Kodunuz ile Three.js kodu arasındaki temel fark, Three.js kodunda değişkenin tmpObjectyalnızca bir kez başlatılması ve ardından döndürülen işlevin her çağrısı tarafından paylaşılmasıdır.

staticDeğişkenlerin C benzeri dillerde nasıl kullanıldığına benzer şekilde, çağrılar arasında bazı durumları korumak için bu yararlı olacaktır .

tmpObject yalnızca iç işlev tarafından görülebilen özel bir değişkendir.

Bellek kullanımını değiştirir, ancak bellekten tasarruf etmek için tasarlanmamıştır.


5

Bu ilginç konuya, tüm yöntemlerin ve değişkenlerin açıkça açığa çıkıncaya kadar gizli tutulmasını sağlayan açığa çıkaran modül modeli kavramını genişleterek katkıda bulunmak istiyorum.

görüntü açıklamasını buraya girin

İkinci durumda, toplama yöntemi Calculator.add () olarak adlandırılır;


0

Verilen örnekte, ilk kod parçası foo () işlevine yapılan her çağrı için aynı tmpObject örneğini kullanacaktır; burada ikinci parçadaki gibi tmpObject her seferinde yeni bir örnek olacaktır.

İlk kod parçacığının kullanılmış olmasının bir nedeni, tmpObject değişkeninin, değeri foo () 'nun bildirildiği kapsama sızmadan foo () çağrıları arasında paylaşılabilmesidir.

İlk kod parçacığının hemen çalıştırılmayan işlev sürümü aslında şöyle görünecektir:

var tmpObject = new Bar();

function foo(){
    // Use tmpObject.
}

Ancak bu sürümün foo () ile aynı kapsamda tmpObject'e sahip olduğuna dikkat edin, bu nedenle daha sonra değiştirilebilir.

Aynı işlevselliği elde etmenin daha iyi bir yolu, ayrı bir modül kullanmaktır:

Modül 'foo.js':

var tmpObject = new Bar();

module.exports = function foo(){
    // Use tmpObject.
};

Modül 2:

var foo = require('./foo');

Bir IEF ile adlandırılmış bir foo oluşturucu işlevinin performansı arasında bir karşılaştırma: http://jsperf.com/ief-vs-named-function


3
"Daha iyi" örneğiniz yalnızca NodeJS'de çalışır ve nasıl daha iyi olduğunu açıklamadınız.
Benjamin Gruenbaum

Ayrı bir modül yapmak "daha iyi" değil, sadece farklı. Özellikle, üst düzey işlevleri birinci dereceden nesnelere daraltmanın bir yoludur. Birinci dereceden kodun ilerlemesi daha kolay olma eğilimindedir, ancak genellikle daha ayrıntılıdır ve bizi ara sonuçları yeniden düzenlemeye zorlar.
Warbo

@BenjaminGruenbaum Modülleri yalnızca Node'da değildir, örneğin browsererify gibi birçok istemci tarafı modül çözümü vardır. Modül çözümünün "daha iyi" olduğunu düşünüyorum çünkü daha okunabilir, hata ayıklaması daha kolay ve kapsamda ne olduğu ve nerede olduğu konusunda daha açık.
Kory Nunn
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.