Son zamanlarda Javascript bir sürü okudum ve tüm dosya ithal .js dosyalarında aşağıdaki gibi sarılmış olduğunu fark edilmiştir.
(function() {
...
code
...
})();
Basit bir yapıcı işlevi kümesi yerine bunu yapmanın nedeni nedir?
Son zamanlarda Javascript bir sürü okudum ve tüm dosya ithal .js dosyalarında aşağıdaki gibi sarılmış olduğunu fark edilmiştir.
(function() {
...
code
...
})();
Basit bir yapıcı işlevi kümesi yerine bunu yapmanın nedeni nedir?
Yanıtlar:
Genellikle ad alanı (daha sonra bakın) ve üye işlevlerinin ve / veya değişkenlerin görünürlüğünü kontrol eder. Bunu bir nesne tanımı gibi düşünün. Bunun teknik adı Hemen Çağırılmış İşlev İfadesidir Çağırılmış İfadesidir (IIFE). jQuery eklentileri genellikle bu şekilde yazılır.
Javascript'te işlevleri iç içe yerleştirebilirsiniz. Dolayısıyla, aşağıdakiler yasaldır:
function outerFunction() {
function innerFunction() {
// code
}
}
Şimdi arayabilirsiniz outerFunction()
, ancak görünürlüğü innerFunction()
kapsamı ile sınırlıdır outerFunction()
, yani özeldir outerFunction()
. Temel olarak Javascript'teki değişkenlerle aynı prensibi izler:
var globalVariable;
function someFunction() {
var localVariable;
}
Buna:
function globalFunction() {
var localFunction1 = function() {
//I'm anonymous! But localFunction1 is a reference to me!
};
function localFunction2() {
//I'm named!
}
}
Yukarıdaki senaryoda, globalFunction()
herhangi bir yerden arama yapabilirsiniz , ancak localFunction1
veya öğesini arayamazsınız localFunction2
.
Yazarken yaptığınız şey (function() { ... })()
, ilk parantez kümesindeki kodu bir işlev değişmezi yapmaktır (yani tüm "nesne" aslında bir işlevdir). Bundan sonra, ()
az önce tanımladığınız işlevi (son ) kendiniz çağırıyorsunuz . Daha önce de belirttiğim gibi, bunun en büyük avantajı, özel yöntemlere / işlevlere ve özelliklere sahip olabilmenizdir:
(function() {
var private_var;
function private_function() {
//code
}
})();
İlk örnekte, globalFunction
çalıştırmak için açıkça ada göre çağırırsınız . Yani, sadece globalFunction()
onu çalıştırmak için yapardın . Ancak yukarıdaki örnekte, sadece bir işlevi tanımlamakla kalmıyorsunuz; Eğer tanımlarken ve tek seferde onu çağırarak. Bu, JavaScript dosyanız yüklendiğinde hemen yürütüldüğü anlamına gelir. Elbette şunları yapabilirsiniz:
function globalFunction() {
// code
}
globalFunction();
Önemli bir fark dışında davranış büyük ölçüde aynı olacaktır: IIFE kullandığınızda küresel kapsamı kirletmekten kaçınıyorsunuz (sonuç olarak, bir adı olmadığı için işlevi birden çok kez çağıramayacağınız anlamına gelir, ancak bu işlev yalnızca gerçekten bir sorun olmadığında yürütülmelidir).
IIFE'ler ile temiz olan şey, içerideki şeyleri de tanımlayabilmeniz ve sadece istediğiniz dünyayı dış dünyaya gösterebilmenizdir (temel olarak kendi kitaplığınızı / eklentinizi oluşturabilmeniz için bir ad alanı örneği):
var myPlugin = (function() {
var private_var;
function private_function() {
}
return {
public_function1: function() {
},
public_function2: function() {
}
}
})()
Şimdi arayabilirsiniz myPlugin.public_function1()
, ancak erişemezsiniz private_function()
! Sınıf tanımına oldukça benzer. Bunu daha iyi anlamak için, daha fazla okuma için aşağıdaki bağlantıları tavsiye ederim:
DÜZENLE
Bahsetmeyi unuttum. Bu finalde ()
, istediğiniz her şeyi aktarabilirsiniz. Örneğin, jQuery eklentileri oluşturduğunuzda, bunu iletir jQuery
veya $
beğenirsiniz:
(function(jQ) { ... code ... })(jQuery)
Burada yaptığınız şey, bir parametreyi alan bir işlev tanımlamaktır ( jQ
yerel bir değişken olarak adlandırılır ve yalnızca bu işlev tarafından bilinir ). Daha sonra işlevi kendiniz çağırırsınız ve bir parametre geçirirsiniz (aynı zamanda denir jQuery
, ancak bu dış dünyadan ve gerçek jQuery'nin kendisine bir referanstır). Bunu yapmak için acil bir ihtiyaç yoktur, ancak bazı avantajları vardır:
Daha önce bu işlevlerin başlangıçta otomatik olarak nasıl çalıştığını açıkladım, ancak otomatik olarak çalışırlarsa argümanları kim iletiyor? Bu teknik, ihtiyacınız olan tüm parametrelerin zaten global değişkenler olarak tanımlandığını varsayar. Dolayısıyla jQuery zaten global bir değişken olarak tanımlanmamışsa bu örnek çalışmaz. Tahmin edebileceğiniz gibi, jquery.js'nin başlatılması sırasında yaptığı şeylerden biri, 'jQuery' global değişkeni ve jQuery dahil edildikten sonra bu kodun çalışmasına izin veren daha ünlü '$' global değişkeni tanımlamaktır.
;(function(jQ) { ... code ... })(jQuery);
Bu şekilde, biri komut dosyasında noktalı virgül bıraktıysa, özellikle kodunuzu küçültmeyi ve başkalarıyla birleştirmeyi planlıyorsanız, sizinkini kırmaz.
(function (context) { ..... })(this)
daha sonra ana içeriğe istediğiniz bir şeyi eklemenizi ve böylece açığa çıkarmanızı sağlar.
En basit haliyle, bu teknik kodu bir fonksiyon kapsamı içine sarmayı amaçlamaktadır .
Şansını azaltmaya yardımcı olur:
O değil belge hazır olduğunda algılamak - bu çeşit değildir document.onload
, ne dewindow.onload
Genellikle bir Immediately Invoked Function Expression (IIFE)
veya olarak bilinir Self Executing Anonymous Function
.
var someFunction = function(){ console.log('wagwan!'); };
(function() { /* function scope starts here */
console.log('start of IIFE');
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})(); /* function scope ends */
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Yukarıdaki örnekte, işlevde tanımlanan herhangi bir değişken (yani kullanılarak bildirildi var
) "özel" olacak ve SADECE (Vivin Paliath'in belirttiği gibi) işlev kapsamı içinde erişilebilir olacaktır. Başka bir deyişle, bu değişkenler fonksiyonun dışında görünmez / ulaşılamaz. Canlı demoya bakın .
Javascript fonksiyon kapsam belirleme özelliğine sahiptir. Msgstr "Bir fonksiyonda tanımlanan parametreler ve değişkenler fonksiyonun dışında görünmez ve fonksiyon içinde herhangi bir yerde tanımlanan bir değişken fonksiyonun her yerinde görülebilir." ("Javascript: İyi Parçalar" dan).
Sonunda, daha önce yayınlanan kod aşağıdaki gibi de yapılabilir:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
};
myMainFunction(); // I CALL "myMainFunction" FUNCTION HERE
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Bir gün biri muhtemelen "myMainFunction" isminden kaçınmanın bir yolu olmalı, çünkü istediğimiz tek şey onu hemen uygulamaktır. "
Temel bilgilere geri dönerseniz, şunu öğrenirsiniz:
expression
: bir değeri değerlendiren bir şey. yani3+11/x
statement
: Satır bir şey yapıyor kodunun (s) AMA yok değil bir değere değerlendirir. yaniif(){}
Benzer şekilde, fonksiyon ifadeleri bir değer olarak değerlendirilir. Ve bir sonuç (sanırım?) Derhal çağrılabilirler:
var italianSayinSomething = function(){ console.log('mamamia!'); }();
Böylece daha karmaşık örneğimiz şöyle olur:
var someFunction = function(){ console.log('wagwan!'); };
var myMainFunction = function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
}();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Bir sonraki adım "neden var myMainFunction =
kullanmasak bile!"
Cevap basit: aşağıdaki gibi bunu kaldırmayı deneyin:
function(){ console.log('mamamia!'); }();
Çünkü işe yaramaz "fonksiyonu bildirimleri Invokable değiliz" .
Hüner kaldırarak olmasıdır var myMainFunction =
biz dönüştürülmüş işlev ifadesi bir içine işlev bildiriminde . Bununla ilgili daha fazla ayrıntı için "Kaynaklar" daki bağlantılara bakın.
Sonraki soru şudur: "Bunu neden başka bir şeyle bir işlev ifadesi olarak tutamıyorum var myMainFunction =
?
Cevap "yapabilirsin" ve aslında bunu yapmanın pek çok yolu var: a +
, a !
, a -
veya belki de bir çift parantez (şimdi konvansiyon tarafından yapıldığı gibi) eklenmesi ve daha fazlasına inanıyorum. Örnek olarak:
(function(){ console.log('mamamia!'); })(); // live demo: jsbin.com/zokuwodoco/1/edit?js,console.
veya
+function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wuwipiyazi/1/edit?js,console
veya
-function(){ console.log('mamamia!'); }(); // live demo: jsbin.com/wejupaheva/1/edit?js,console
Bu nedenle, bir zamanlar "Alternatif Kodumuza" ilgili değişiklik eklendikten sonra, "Kod Açıklaması" örneğinde kullanılanla aynı koda dönüyoruz.
var someFunction = function(){ console.log('wagwan!'); };
(function() {
console.log('start of IIFE');
var myNumber = 4;
var myFunction = function(){ console.log('formidable!'); };
var myObject = {
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
console.log('end of IIFE');
})();
someFunction(); // reachable, hence works: see in the console
myFunction(); // unreachable, will throw an error, see in the console
myObject.anotherFunc(); // unreachable, will throw an error, see in the console
Hakkında daha fazla bilgi Expressions vs Statements
:
Merak edebileceğiniz bir şey, "fonksiyonun içinde 'düzgün' değişkeni TANIMLAMADIĞINIZDA ne olur - yani bunun yerine basit bir atama yapın?"
(function() {
var myNumber = 4; /* number variable declaration */
var myFunction = function(){ /* function variable declaration */
console.log('formidable!');
};
var myObject = { /* object variable declaration */
anotherNumber : 1001,
anotherFunc : function(){ console.log('formidable!'); }
};
myOtherFunction = function(){ /* oops, an assignment instead of a declaration */
console.log('haha. got ya!');
};
})();
myOtherFunction(); // reachable, hence works: see in the console
window.myOtherFunction(); // works in the browser, myOtherFunction is then in the global scope
myFunction(); // unreachable, will throw an error, see in the console
Temel olarak, geçerli kapsamı içinde bildirilmeyen bir değişkene bir değer atanırsa, "değişkeni bulana veya global kapsama (hangi noktada oluşturacağı) ulaşıncaya kadar kapsam zincirine bakın".
Bir tarayıcı ortamında (düğümler gibi bir sunucu ortamına karşı) global kapsam window
nesne tarafından tanımlanır . Böylece yapabiliriz window.myOtherFunction()
.
Bu konuyla ilgili "İyi uygulamalar" ipucum, her var
zaman bir şey tanımlarken kullanmaktır : ister sayı, ister nesne veya işlev, ister global kapsamda olsa bile. Bu, kodu çok daha basit hale getirir.
Not:
block scope
(: eklenen blok kapsamı yerel değişkenler Güncelleme ES6 .)function scope
& vardır global scope
(( window
tarayıcı ortamında kapsam)Hakkında daha fazla bilgi Javascript Scopes
:
Bu IIFE
kavramı elde ettikten sonra module pattern
, genellikle bu IIFE modelini kullanarak yapılır. İyi eğlenceler :)
Bir tarayıcıdaki Javascript'in gerçekten birkaç etkili kapsamı vardır: işlev kapsamı ve küresel kapsam.
Bir değişken işlev kapsamında değilse, global kapsamdadır. Ve global değişkenler genellikle kötüdür, bu yüzden bu kütüphane değişkenlerini kendine saklayan bir yapıdır.
Buna kapanış denir. Temelde, fonksiyonun içindeki kodu mühürler, böylece diğer kütüphaneler buna müdahale etmez. Derlenmiş dillerde ad alanı oluşturmaya benzer.
Misal. Diyelim ki yazıyorum:
(function() {
var x = 2;
// do stuff with x
})();
Şimdi diğer kütüphaneler kütüphanemde x
kullanmak için oluşturduğum değişkene erişemiyor .
(function(){ ... return { publicProp1: 'blah' }; })();
. Açıkçası isimlendirmeye mükemmel bir şekilde paralel değil, ama bu şekilde düşünmeye yardımcı olabilir.
Bazı html5 nesneleri için tarayıcı desteğini belirleme yönteminde olduğu gibi, işlev kapanışlarını da daha büyük ifadelerde veri olarak kullanabilirsiniz .
navigator.html5={
canvas: (function(){
var dc= document.createElement('canvas');
if(!dc.getContext) return 0;
var c= dc.getContext('2d');
return typeof c.fillText== 'function'? 2: 1;
})(),
localStorage: (function(){
return !!window.localStorage;
})(),
webworkers: (function(){
return !!window.Worker;
})(),
offline: (function(){
return !!window.applicationCache;
})()
}
Değişkenleri yerel tutmanın yanı sıra, çok kullanışlı bir kullanım, global bir değişken kullanarak bir kitaplık yazarken, kitaplıkta kullanmak için daha kısa bir değişken adı verebilirsiniz. JQuery jQuery.noConflict () kullanarak jQuery'ye işaret eden $ değişkenini devre dışı bırakmanıza izin verdiğinden, genellikle jQuery eklentileri yazarken kullanılır. Devre dışı bırakılmışsa, kodunuz yine de $ kullanabilir ve yalnızca şunları yaparsanız kesilemez:
(function($) { ...code...})(jQuery);
Ayrıca, kodun "katı modda" yürütülmesi için kapsam işlevinde 'katı kullan' kullanmalıyız. Aşağıda gösterilen örnek kod
(function() {
'use strict';
//Your code from here
})();