Döngülerin içinde JavaScript kapatma - basit pratik örnek


2817

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

Bunu çıktılar:

Değerim: 3
Değerim: 3
Değerim: 3

Oysa ben çıktı istiyorum:

Değerim: 0
Değerim: 1
Değerim: 2


Aynı sorun, işlevi çalıştırma gecikmesine olay dinleyicileri neden olduğunda da oluşur:

var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i++) {
  // as event listeners
  buttons[i].addEventListener("click", function() {
    // each should log its value.
    console.log("My value: " + i);
  });
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>

… Veya eşzamansız kod, örneğin Promises kullanımı:

// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for (var i = 0; i < 3; i++) {
  // Log `i` as soon as each promise resolves.
  wait(i * 100).then(() => console.log(i));
}

Düzenleme: Ayrıca görünür for inve for ofdöngüler:

const arr = [1,2,3];
const fns = [];

for(i in arr){
  fns.push(() => console.log(`index: ${i}`));
}

for(v of arr){
  fns.push(() => console.log(`value: ${v}`));
}

for(f of fns){
  f();
}

Bu temel sorunun çözümü nedir?


55
funcsSayısal indeksler kullanıyorsanız dizi olmak istemediğinizden emin misiniz ? Sadece aklınızda bulunsun.
DanMan

23
Bu gerçekten kafa karıştırıcı bir problem. Bu makale anlamama yardımcı oldu . Başkalarına da yardımcı olabilir.
user3199690

4
Başka bir basit ve açıklanmış çözüm: 1) İç İçe İşlevler , bunların üstündeki "üst" kapsama erişebilir ; 2) bir kapatma çözümü ... "Bir kapatma, ana işlev kapatıldıktan sonra bile ana kapsama erişime sahip bir işlevdir".
Peter Krauss


35
Gelen ES6 , önemsiz bir çözelti değişken beyan etmektir i ile let döngü gövdesinde için kapsamlı.
Tomas Nikodym

Yanıtlar:


2147

Sorun, ianonim işlevlerinizin her birinde değişkenin , işlevin dışındaki aynı değişkene bağlı olmasıdır.

Klasik çözüm: Kapaklar

Yapmak istediğiniz her bir fonksiyon içindeki değişkeni fonksiyonun dışındaki ayrı, değişmeyen bir değere bağlamaktır:

var funcs = [];

function createfunc(i) {
  return function() {
    console.log("My value: " + i);
  };
}

for (var i = 0; i < 3; i++) {
  funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

JavaScript - yalnızca işlev kapsamı içinde blok kapsamı olmadığından, işlev oluşturmayı yeni bir işlevde sararak, "i" değerinin istediğiniz gibi kalmasını sağlarsınız.


ES5.1 çözümü: forEach

Array.prototype.forEachFonksiyonun nispeten yaygın kullanılabilirliği ile (2015'te), öncelikle bir değer dizisi üzerinde yinelemeyi içeren durumlarda, .forEach()her yineleme için ayrı bir kapanış elde etmek için temiz, doğal bir yol sağladığını belirtmek gerekir. Yani, değerler içeren bir dizi diziniz olduğu (DOM referansları, nesneler, ne olursa olsun) ve her bir öğeye özgü geri aramaların ayarlanmasından kaynaklanan sorun varsa, bunu yapabilirsiniz:

var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
  // ... code code code for this one element
  someAsynchronousFunction(arrayElement, function() {
    arrayElement.doSomething();
  });
});

Fikir, .forEachdöngü ile kullanılan geri çağrı işlevinin her çağrılmasının kendi kapanışı olacağıdır. Bu işleyiciye iletilen parametre, yinelemenin söz konusu adımına özgü dizi öğesidir. Eşzamansız bir geri çağrıda kullanılırsa, yinelemenin diğer adımlarında oluşturulan diğer geri çağrılarla çarpışmaz.

JQuery'de çalışıyorsanız, $.each()işlev size benzer bir yetenek verir.


ES6 çözümü: let

ECMAScript 6 (ES6), taban değişkenlerden farklı kapsamda yeni letve constanahtar kelimeler sunar var. Örneğin, lettabanlı bir dizine sahip bir döngüde, döngüdeki her yinelemenin idöngü kapsamına sahip yeni bir değişkeni olacaktır , bu nedenle kodunuz beklediğiniz gibi çalışır. Birçok kaynak var, ancak 2ality'nin blok kapsamındaki yazıyı büyük bir bilgi kaynağı olarak öneriyorum .

for (let i = 0; i < 3; i++) {
  funcs[i] = function() {
    console.log("My value: " + i);
  };
}

Yine de, IE9-IE11 ve Edge'in Edge 14'ten önce desteklediğine dikkat edin, ancak letyukarıdakileri yanlış alın ( iher seferinde yeni bir tane oluşturmazlar , bu nedenle yukarıdaki tüm işlevler, kullandığımız gibi 3'ü kaydeder var). Edge 14 sonunda bunu düzeltiyor.


7
function createfunc(i) { return function() { console.log("My value: " + i); }; }değişkeni kullandığından hala kapatma değil imi?
ス レ ッ ク

55
Ne yazık ki, bu cevap eski ve hiç kimse en altta doğru cevabı görmeyecek - kullanmak Function.bind()şimdiye kadar kesinlikle tercih edilir, bkz. Stackoverflow.com/a/19323214/785541 .
Wladimir Palant

81
@Wladimir: Öneriniz .bind()olan "doğru cevap" doğru değil. Her birinin kendi yeri var. İle değeri .bind()bağlamadan argümanları bağlayamazsınız this. Ayrıca iargümanın bir kopyasını bazen gerekli olan çağrılar arasında değiştirme yeteneği olmadan alırsınız . Dolayısıyla, .bind()uygulamaların tarihsel olarak yavaş olduğunu belirtmemek için oldukça farklı yapılar . Elbette basit örnekte de işe yarayacaktır, ancak kapanışlar anlaşılması gereken önemli bir kavramdır ve sorunun konusu buydu.
çerez canavarı

8
Lütfen bu dönüş işlevi kesmek için kullanmayı bırakın, aynı kapsam değişkenlerini yeniden kullanmaktan kaçınmak yerine [] .forEach veya [] .map kullanın.
Christian Landgren

32
@ChristianLandgren: Bu sadece bir Array yineliyorsanız faydalıdır. Bu teknikler "hack" değildir. Bunlar temel bilgidir.

379

Deneyin:

var funcs = [];
    
for (var i = 0; i < 3; i++) {
    funcs[i] = (function(index) {
        return function() {
            console.log("My value: " + index);
        };
    }(i));
}

for (var j = 0; j < 3; j++) {
    funcs[j]();
}

Düzenle (2014):

Şahsen ben @ Aust'in kullanımla ilgili daha yeni cevabı.bind şimdi böyle bir şey yapmanın en iyi yolu olduğunu düşünüyorum. Ayrıca 's _.partialile uğraşmak ya da karışıklık istediğinizde lo-dash / alt çizgi bind' s thisArg.


2
hakkında herhangi bir açıklama }(i));?
aswzen

3
@aswzen Bence bu işleve iargüman olarak geçiyor index.
Jet Blue

aslında yerel değişken indeksi yaratıyor.
Abhishek Singh

1
Hemen Fonksiyon İfadesini, yani IIFE'yi çağırın. (i) hemen çağrılan ve dizin i'den ayarlanan anonim işlev ifadesinin bağımsız değişkenidir.
Yumurtalar

348

Henüz bahsedilmeyen bir başka yol da Function.prototype.bind

var funcs = {};
for (var i = 0; i < 3; i++) {
  funcs[i] = function(x) {
    console.log('My value: ' + x);
  }.bind(this, i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();
}

GÜNCELLEME

@Squint ve @mekdev'in de işaret ettiği gibi, önce döngü dışında işlev oluşturarak ve ardından döngü içindeki sonuçları bağlayarak daha iyi performans elde edersiniz.

function log(x) {
  console.log('My value: ' + x);
}

var funcs = [];

for (var i = 0; i < 3; i++) {
  funcs[i] = log.bind(this, i);
}

for (var j = 0; j < 3; j++) {
  funcs[j]();
}


Ben de bu günlerde yaptığım şey, ben de lo-dash / underscore's gibi_.partial
Bjorn

17
.bind()ECMAScript 6 özellikleri ile büyük ölçüde kullanılmayacak. Ayrıca, bu aslında yineleme başına iki işlev yaratır. Önce anonim sonra da anonim .bind(). Döngü dışında, sonra .bind()içeride oluşturmak daha iyi bir kullanım olacaktır .

5
@squint @mekdev - İkiniz de haklısınız. İlk örneğim, nasıl bindkullanıldığını göstermek için hızlı bir şekilde yazılmıştır . Önerilerinize başka bir örnek daha ekledim.
Aust

5
İki O (n) döngü üzerinde hesaplama israf yerine, sadece (var i = 0; i <3; i ++) {log.call (this, i); }
user2290820

1
.bind (), kabul edilen cevabın PLUS ile uğraştığını gösterir this.
niry

269

Bir Dizin değişkenini kuşatmanın en basit ve en okunabilir yolu: Hemen Çağırılmış İşlev İfadesi kullanarak :

for (var i = 0; i < 3; i++) {

    (function(index) {

        console.log('iterator: ' + index);
        //now you can also loop an ajax call here 
        //without losing track of the iterator value:   $.ajax({});
    
    })(i);

}

Bu, yineleyiciyi itanımladığımız anonim işleve gönderir index. Bu, değişkenin iIIFE içindeki herhangi bir eşzamansız işlevsellikte daha sonra kullanılmak üzere kaydedildiği bir kapak oluşturur .


10
Daha fazla kod okunabilirliği ve hangisinin iolduğu konusunda karışıklığı önlemek için function parametresini yeniden adlandırırım index.
Kyle Falconer

5
Orijinal soruda açıklanan dizi işlevlerini tanımlamak için bu tekniği nasıl kullanırsınız ?
Nico

@Nico Orijinal soruda gösterildiği gibi, indexbunun yerine kullanmak yerine i.
JLRishe

@JLRishevar funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = (function(index) { return function() {console.log('iterator: ' + index);}; })(i); }; for (var j = 0; j < 3; j++) { funcs[j](); }
Nico

1
@Nico OP'nin özel durumunda, sadece sayılar üzerinde yineleniyorlar, bu yüzden bu harika bir durum olmaz .forEach(), ancak bir dizi ile başlandığında çoğu zaman forEach()iyi bir seçimdir:var nums [4, 6, 7]; var funcs = {}; nums.forEach(function (num, i) { funcs[i] = function () { console.log(num); }; });
JLRishe

164

Partiye biraz geç kaldım, ama bugün bu sorunu araştırıyordum ve cevapların birçoğunun Javascript'in kapsamları nasıl ele aldığını tam olarak ele almadığını fark ettim, bu da esasen bununla ilgilidir.

Diğerlerinin de belirttiği gibi, sorun iç fonksiyonun aynı ideğişkene başvurmasıdır. Öyleyse neden her bir yinelemede yeni bir yerel değişken oluşturmuyoruz ve bunun yerine iç fonksiyona başvuruyor?

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    var ilocal = i; //create a new local variable
    funcs[i] = function() {
        console.log("My value: " + ilocal); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

Daha önce olduğu gibi, her iç fonksiyonun atanan son değeri verdiği yerde i, şimdi her iç fonksiyon sadece atanmış olan son değeri verir ilocal. Ancak her yinelemenin kendine ait olmaması gerekir ilocalmi?

Anlaşılan, sorun bu. Her yineleme aynı kapsamı paylaşır, bu yüzden ilkinden sonraki her yineleme sadece üzerine yazılır ilocal. Gönderen MDN'yi :

Önemli: JavaScript'in blok kapsamı yoktur. Bir blokla eklenen değişkenler, içeren fonksiyona veya komut dosyasına dahil edilir ve bunları ayarlama etkileri, bloğun kendisinin ötesinde kalır. Başka bir deyişle, blok ifadeler bir kapsam sunmaz. "Bağımsız" bloklar geçerli sözdizimi olsa da, JavaScript'te bağımsız bloklar kullanmak istemezsiniz, çünkü C veya Java'da bu tür bloklar gibi bir şey yaptığını düşünüyorsanız, yaptıklarını düşündüklerini yapmazlar.

Vurgu için tekrarlandı:

JavaScript'in blok kapsamı yoktur. Bir blokla sunulan değişkenler, içeren fonksiyona veya komut dosyasına dahil edilir

Bunu ilocalher bir yinelemede beyan etmeden önce kontrol ederek görebiliriz :

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
  console.log(ilocal);
  var ilocal = i;
}

İşte bu yüzden bu hata çok zor. Bir değişkeni yeniden bildiriyor olsanız bile, Javascript bir hata atmaz ve JSLint bir uyarı bile atmaz. Bu yüzden bunu çözmenin en iyi yolu, temel olarak Javascript'te iç fonksiyonların dış değişkenlere erişebileceği fikridir, çünkü iç kapsamlar dış kapsamları "içine alır".

Kapaklar

Bu aynı zamanda, dış fonksiyonlar geri gelse bile iç fonksiyonların dış değişkenleri "tutunması" ve canlı tutulması anlamına gelir. Bunu kullanmak için, yalnızca yeni bir kapsam oluşturmak, yeni kapsamda beyan etmek ilocalve kullanan bir iç işlev döndürmek için bir sarmalayıcı işlevi oluşturur ve çağırırız ilocal(daha fazla açıklama aşağıdadır):

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    funcs[i] = (function() { //create a new scope using a wrapper function
        var ilocal = i; //capture i into a local var
        return function() { //return the inner function
            console.log("My value: " + ilocal);
        };
    })(); //remember to run the wrapper function
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}

Bir sarıcı işlevi içinde iç işlev oluşturmak, iç işleve yalnızca erişebileceği özel bir ortam, bir "kapatma" sağlar. Böylece, sarma işlevini her çağırdığımızda, kendi ayrı ortamıyla yeni bir iç işlev yaratırız, bu da ilocaldeğişkenlerin birbiriyle çarpışmamasını ve üzerine yazmamasını sağlar. Birkaç küçük optimizasyon, diğer birçok SO kullanıcısının verdiği nihai cevabı verir:

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (var i = 0; i < 3; i++) {
    funcs[i] = wrapper(i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();
}
//creates a separate environment for the inner function
function wrapper(ilocal) {
    return function() { //return the inner function
        console.log("My value: " + ilocal);
    };
}

Güncelleme

ES6 artık genel kullanıma sunulduğunda, artık letblok kapsamındaki değişkenler oluşturmak için yeni anahtar kelimeyi kullanabiliriz :

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML += '<p>' + msg + '</p>';};

var funcs = {};
for (let i = 0; i < 3; i++) { // use "let" to declare "i"
    funcs[i] = function() {
        console.log("My value: " + i); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j++) { // we can use "var" here without issue
    funcs[j]();
}

Bak şimdi ne kadar kolay! Daha fazla bilgi için bilgilerimin dayandığı bu yanıta bakın .


IIFE yolunu nasıl açıkladığınızı da seviyorum. Bunu arıyordum. Teşekkür ederim.
CapturedTree

4
Artık letve constanahtar kelimelerini kullanarak JavaScript'te blok kapsam belirleme gibi bir şey var . Eğer bu cevap bunu içerecek şekilde genişleseydi, bence küresel olarak çok daha yararlı olurdu.

@TinyGiant emin bir şey hakkında bazı bilgiler ekledim letve daha eksiksiz bir açıklama
bağladım

@ woojoo666 Cevabınız aşağıdaki gibi iki alternatif URL'yi çağırmak için de kullanılabilir i=0; while(i < 100) { setTimeout(function(){ window.open("https://www.bbc.com","_self") }, 3000); setTimeout(function(){ window.open("https://www.cnn.com","_self") }, 3000); i++ }mi? (window.open () 'i getelementbyid ile değiştirebilir ......)
natty hakkında ceviz

@nuttyaboutnatty bu kadar geç bir cevap için üzgünüm. Örneğinizdeki kod zaten çalışıyor gibi görünmüyor. iZaman aşımı işlevlerinizde kullanmıyorsunuz , bu yüzden kapanmaya ihtiyacınız yok
woojoo666

151

ES6 artık geniş çapta desteklendiğinde, bu sorunun en iyi yanıtı değişti. ES6 bu durum için letve constanahtar sözcüklerini sağlar . Kapaklarla uğraşmak yerine, sadece şöyle letbir döngü kapsamı değişkeni ayarlamak için kullanabiliriz :

var funcs = [];

for (let i = 0; i < 3; i++) {          
    funcs[i] = function() {            
      console.log("My value: " + i); 
    };
}

valdaha sonra, döngünün belirli bir dönüşüne özgü bir nesneyi işaret eder ve ek kapatma gösterimi olmadan doğru değeri döndürür. Bu açıkça bu sorunu önemli ölçüde basitleştirir.

constletdeğişken adının ilk atamadan sonra yeni bir referansa geri yüklenemeyeceği ek kısıtlamaya benzer .

Tarayıcı desteği, tarayıcıların en son sürümlerini hedefleyen kullanıcılar için artık burada. const/ letşu anda en yeni Firefox, Safari, Edge ve Chrome'da desteklenmektedir. Ayrıca Düğüm'de desteklenir ve Babel gibi oluşturma araçlarından yararlanarak her yerde kullanabilirsiniz. Burada çalışan bir örnek görebilirsiniz: http://jsfiddle.net/ben336/rbU4t/2/

Dokümanlar burada:

Yine de, IE9-IE11 ve Edge'in Edge 14'ten önce desteklediğine dikkat edin, ancak letyukarıdakileri yanlış alın ( iher seferinde yeni bir tane oluşturmazlar , bu nedenle yukarıdaki tüm işlevler, kullandığımız gibi 3'ü kaydeder var). Edge 14 sonunda bunu düzeltiyor.


Maalesef 'let', özellikle mobil cihazlarda hala tam olarak desteklenmemektedir. developer.mozilla.org/tr-TR/docs/Web/JavaScript/Reference/…
MattC

2
Haziran '16 itibariyle let Evergreen tarayıcılar bunu destekleyecek iOS Safari, Opera Mini ve Safari 9. hariç tüm büyük tarayıcı sürümleri desteklenir. Babel, yüksek uyumluluk modu açılmadan beklenen davranışı sürdürmeyi doğru bir şekilde aktarır.
Dan Pantry

@DanPantry evet güncelleme zamanı hakkında :) Const, doc bağlantıları ve daha iyi uyumluluk bilgileri eklenmesi de dahil olmak üzere, mevcut durumun daha iyi yansıtmak için güncellendi.
Ben McCormick

ES6 / 7'yi desteklemeyen tarayıcıların neler olduğunu anlayabilmesi için kodumuzu aktarmak için babil'i kullanmamızın nedeni bu değil mi?
piksel 67

87

Bunu söylemenin başka bir yolu i, fonksiyonunuzdaki fonksiyonun, fonksiyonun yaratılması sırasında değil, fonksiyonun yaratılması sırasında bağlı olmasıdır.

Kapatma ioluşturduğunuzda, kapama oluştururken olduğu gibi bir kopyası değil, dış kapsamda tanımlanan değişkene bir başvurudır. Uygulama sırasında değerlendirilecektir.

Diğer yanıtların çoğu, sizin için değeri değiştirmeyecek başka bir değişken oluşturarak geçici çözüm yolları sunar.

Sadece netlik için bir açıklama ekleyeceğimi düşündüm. Bir çözüm için, şahsen, Harto'yla birlikte giderdim, çünkü buradaki cevaplardan bunu yapmanın en açıklayıcı yolu. Gönderilen kodlardan herhangi biri işe yarayacaktır, ancak neden yeni bir değişken (Freddy ve 1800'ler) beyan ettiğimi veya garip gömülü kapatma sözdizimine (apphacker) açıklamak için bir yığın yorum yazmak zorunda kalmadan bir kapanış fabrikası tercih ediyorum.


71

Anlamanız gereken şey, Javascript'teki değişkenlerin kapsamının işleve dayanmasıdır. Bu, blok kapsamına sahip olduğunuz c # 'dan daha önemli bir farktır ve sadece değişkeni for içinde birine kopyalamak işe yarayacaktır.

Değişkenin şimdi işlev kapsamına sahip olması nedeniyle, apphacker'ın yanıtı gibi işlevi döndürmeyi değerlendiren bir işlevde kaydırmak hile yapar.

Blok kapsam kuralının kullanılmasına izin veren var yerine bir let anahtar sözcüğü de vardır. Bu durumda for içinde bir değişken tanımlamak hile yapar. Bununla birlikte, let anahtar sözcüğü uyumluluk nedeniyle pratik bir çözüm değildir.

var funcs = {};

for (var i = 0; i < 3; i++) {
  let index = i; //add this
  funcs[i] = function() {
    console.log("My value: " + index); //change to the copy
  };
}

for (var j = 0; j < 3; j++) {
  funcs[j]();
}


@nickf hangi tarayıcı? Dediğim gibi, uyumluluk sorunları var, demek istiyorum ki IE'de desteklendiğini sanmıyorum gibi ciddi uyumluluk sorunları.
eglasius

1
@ nickf evet, şu referansı kontrol edin: developer.mozilla.org/En/New_in_JavaScript_1.7 ... izin tanımları bölümüne bakın, bir döngü içinde onclick örneği var
eglasius

2
@nickf hmm, aslında sürümü açıkça belirtmelisiniz: <script type = "application / javascript; version = 1.7" /> ... IE kısıtlaması nedeniyle hiçbir yerde kullanmadım, sadece değil pratik :(
eglasius

farklı sürümler için tarayıcı desteğini buradan görebilirsiniz es.wikipedia.org/wiki/Javascript
eglasius


59

Bu, Bjorn'un (apphacker) benzeri, teknikte, değişken değerini, parametre olarak geçirmektense bir parametre olarak atamak yerine atamanıza izin veren başka bir varyasyon:

var funcs = [];
for (var i = 0; i < 3; i++) {
    funcs[i] = (function() {
        var index = i;
        return function() {
            console.log("My value: " + index);
        }
    })();
}

Hangi tekniği kullanırsanız kullanın, indexdeğişkenin iç fonksiyonun döndürülen kopyasına bağlı bir tür statik değişken haline geldiğini unutmayın. Yani, değerindeki değişiklikler çağrılar arasında korunur. Çok kullanışlı olabilir.


Teşekkürler ve çözümünüz işe yarıyor. Ama bunun neden işe yaradığını sormak istiyorum, ancak varçizgiyi ve çizgiyi değiştirmek returnişe yaramaz mı? Teşekkürler!
midnite

@midnite Değiştirdiyseniz varve returnsonra değişken iç işlevi döndürmeden önce atanmazsa .
Boann

53

Bu, JavaScript'te kapak kullanımıyla ilgili yaygın hatayı açıklar.

İşlev yeni bir ortam tanımlar

Düşünmek:

function makeCounter()
{
  var obj = {counter: 0};
  return {
    inc: function(){obj.counter ++;},
    get: function(){return obj.counter;}
  };
}

counter1 = makeCounter();
counter2 = makeCounter();

counter1.inc();

alert(counter1.get()); // returns 1
alert(counter2.get()); // returns 0

Her makeCounterçağrıldığında, {counter: 0}yeni bir nesne oluşturulur. Ayrıca, obj yeni nesneye başvuruda bulunmak için yeni bir kopyası da oluşturulur. Bu nedenle, counter1ve counter2birbirinden bağımsızdır.

Döngülerdeki kapaklar

Döngüde bir kapatma kullanmak zor.

Düşünmek:

var counters = [];

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = {
      inc: function(){obj.counter++;},
      get: function(){return obj.counter;}
    }; 
  }
}

makeCounters(2);

counters[0].inc();

alert(counters[0].get()); // returns 1
alert(counters[1].get()); // returns 1

Bildirim o counters[0]ve counters[1]vardır değil bağımsız. Aslında, aynı şekilde çalışıyorlar obj!

Bunun nedeni obj, belki de performans nedenleriyle, döngünün tüm yinelemelerinde paylaşılan tek bir kopyasının olmasıdır . {counter: 0}Her yinelemede yeni bir nesne oluştursa da , aynı kopyası objen yeni nesneye referansla güncellenir.

Çözüm başka bir yardımcı işlev kullanmaktır:

function makeHelper(obj)
{
  return {
    inc: function(){obj.counter++;},
    get: function(){return obj.counter;}
  }; 
}

function makeCounters(num)
{
  for (var i = 0; i < num; i++)
  {
    var obj = {counter: 0};
    counters[i] = makeHelper(obj);
  }
}

Bunun nedeni, doğrudan işlev kapsamındaki yerel değişkenlere ve işlev bağımsız değişkenlerine girişte yeni kopyalar tahsis edilmesidir.


Küçük açıklama: Döngülerdeki kapanışların ilk örneğinde sayaçlar [0] ve sayaçlar [1] performans nedenlerinden dolayı bağımsız değildir. Bunun nedeni, var obj = {counter: 0};herhangi bir kod yürütülmeden önce değerlendirilmesidir: MDN var : var bildirimleri, nerede olurlarsa olsunlar, herhangi bir kod yürütülmeden önce işlenir.
Charidimos

50

En basit çözüm,

Kullanmak yerine:

var funcs = [];
for(var i =0; i<3; i++){
    funcs[i] = function(){
        alert(i);
    }
}

for(var j =0; j<3; j++){
    funcs[j]();
}

3 kez "2" uyarısı verir. Bunun nedeni, döngü için oluşturulan anonim işlevlerin aynı kapatmayı paylaşması ve bu kapatılmanın değerinin iaynı olmasıdır. Paylaşılan kapanmayı önlemek için bunu kullanın:

var funcs = [];
for(var new_i =0; new_i<3; new_i++){
    (function(i){
        funcs[i] = function(){
            alert(i);
        }
    })(new_i);
}

for(var j =0; j<3; j++){
    funcs[j]();
}

Bunun arkasındaki fikir, for döngüsünün tüm gövdesini bir IIFE (Hemen Çağırılmış Fonksiyon İfadesi) ile kapsüllemek ve new_ibir parametre olarak geçmek ve onu yakalamaktır i. Anonim işlev hemen yürütüldüğünden, ideğer anonim işlev içinde tanımlanan her işlev için farklıdır.

Bu çözüm, bu sorundan muzdarip orijinal kodda minimum değişiklikler gerektireceğinden, bu tür bir soruna uyacak gibi görünüyor. Aslında, bu tasarım gereğidir, hiç bir sorun olmamalı!


2
Bir kitapta benzer bir şeyi bir kez okuyun. Bunu da tercih ediyorum, çünkü mevcut kodunuza (çok fazla) dokunmanız gerekmiyor ve kendi kendine çağırma fonksiyon modelini öğrendikten sonra neden yaptığınızı belli oluyor: yeni oluşturulan bu değişkeni yakalamak için dürbün.
DanMan

1
@DanMan Teşekkürler. Kendini arayan anonim işlevler, javascript'in blok düzeyinde değişken kapsam eksikliği ile başa çıkmanın çok iyi bir yoludur.
Kemal Dağ

3
Kendini çağırmak veya kendini çağırmak bu teknik için uygun terim değildir, IIFE (Hemen Çağırılan İşlev İfadesi) daha doğru olur. Ref: benalman.com/news/2010/11/…
jherax

31

bunu daha kısa olanı dene

  • dizi yok

  • döngü için ekstra yok


for (var i = 0; i < 3; i++) {
    createfunc(i)();
}

function createfunc(i) {
    return function(){console.log("My value: " + i);};
}

http://jsfiddle.net/7P6EN/


1
Çözümünüz doğru çıktı gibi görünüyor, ancak gereksiz yere işlevler kullanıyor, neden sadece console.log çıktı değil? Orijinal soru, aynı kapanışa sahip anonim işlevlerin oluşturulmasıyla ilgilidir. Sorun, tek bir kapanışları olduğu için, i'nin değeri her biri için aynıdır. Umarım anlarsın.
Kemal Dağ

30

İşte basit bir çözüm forEach(IE9'a geri çalışır):

var funcs = [];
[0,1,2].forEach(function(i) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
})
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Baskılar:

My value: 0
My value: 1
My value: 2

27

OP tarafından gösterilen kodla ilgili ana sorun i, ikinci döngüye kadar asla okunmamasıdır. Göstermek için, kodun içinde bir hata gördüğünüzü hayal edin

funcs[i] = function() {            // and store them in funcs
    throw new Error("test");
    console.log("My value: " + i); // each should log its value.
};

Hata funcs[someIndex], yürütülene kadar gerçekleşmez (). Aynı mantığı kullanarak, değerinin ide bu noktaya kadar toplanmadığı anlaşılmalıdır . Orijinal döngü bittiğinde, koşulun başarısız olmasına ve döngü sonlanmasına neden olan değeri i++getirir . Bu noktada, kullanılır ve kullanıldığında ve değerlendirildiğinde, her seferinde 3'tür.i3i < 3i3funcs[someIndex]()i

Bunu aşmak iiçin, karşılaşıldığını değerlendirmelisiniz . Bunun halihazırda funcs[i](3 benzersiz dizin olduğu yerde) şeklinde gerçekleştiğini unutmayın. Bu değeri elde etmenin birkaç yolu vardır. Birincisi, onu burada zaten çeşitli şekillerde gösterilen bir işleve parametre olarak iletmektir.

Başka bir seçenek de değişkeni kapatabilecek bir işlev nesnesi oluşturmaktır. Bu şekilde başarılabilir

jsFiddle Demo

funcs[i] = new function() {   
    var closedVariable = i;
    return function(){
        console.log("My value: " + closedVariable); 
    };
};

23

JavaScript işlevleri, bildirim üzerine eriştikleri kapsamı "kapatır" ve bu kapsamdaki değişkenler değişse bile bu kapsama erişimi korur.

var funcs = []

for (var i = 0; i < 3; i += 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k += 1) {
  funcs[k]()
}

Yukarıdaki dizideki her işlev, global kapsamı kapatır (yalnızca, bildirildikleri kapsam olduğu için global).

Daha sonra bu işlevler i, global kapsamdaki en güncel değerin günlüğe kaydedilmesiyle çağrılır . Kapanmanın büyüsü ve hayal kırıklığı bu.

"JavaScript İşlevleri, bildirildikleri kapsamı kapatır ve kapsamın içindeki değişken değerler değişse bile bu kapsama erişimi korur."

Kullanma letyerine varyeni bir kapsam her oluşturarak çözer, bu for, her bir fonksiyon üzerinde kapatılması için bir ayrı kapsamı oluşturma ilmek çalışır. Diğer çeşitli teknikler de aynı şeyi ekstra işlevlerle yapar.

var funcs = []

for (let i = 0; i < 3; i += 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k += 1) {
  funcs[k]()
}

( letdeğişkenleri blok haline getirir. Bloklar kıvırcık parantezlerle belirtilir, ancak for döngüsü için başlatma değişkeninin, ibizim durumumuzda parantez içinde bildirildiği kabul edilir.)


1
Bu cevabı okuyana kadar bu kavramı anlamaya çalıştım. Gerçekten önemli bir noktaya değiniyor - değeri iküresel kapsamda. Tüm forDöngü tamamlandığında çalışan, küresel değer i(örneğin, kullanarak bu işlevi dizideki çağrılır her nedenle artık 3. olan funcs[j]), ibu fonksiyonda, küresel başvuran i(3,), değişken.
Modermo

13

Çeşitli çözümleri okuduktan sonra, bu çözümlerin çalışmasının nedeninin kapsam zinciri kavramına güvenmek olduğunu eklemek isterim . JavaScript, yürütme sırasında bir değişkeni çözme biçimidir.

  • Her bir işlev tanımı, tarafından bildirilen tüm yerel değişkenleri varve bunun değişkenlerini içeren bir kapsam oluşturur arguments.
  • Başka bir (dış) fonksiyonun içinde tanımlanan iç fonksiyonumuz varsa, bu bir zincir oluşturur ve yürütme sırasında kullanılacaktır
  • Bir işlev yürütüldüğünde, çalışma zamanı değişkenleri kapsam zincirini arayarak değerlendirir . Zincirin belirli bir noktasında bir değişken bulunursa, aramayı durduracak ve kullanacaktır, aksi takdirde ait olduğu küresel kapsama ulaşana kadar devam eder window.

İlk kodda:

funcs = {};
for (var i = 0; i < 3; i++) {         
  funcs[i] = function inner() {        // function inner's scope contains nothing
    console.log("My value: " + i);    
  };
}
console.log(window.i)                  // test value 'i', print 3

Ne zaman funcsişletilirse, kapsam zinciri olacak function inner -> global. Değişken iiçinde bulunamadığından function inner(ne varargümanlar kullanılarak bildirildi ne de argümanlar olarak iletildi), değeri ien sonunda olan global kapsamda bulunana kadar aramaya devam eder window.i.

Bir dış fonksiyonunda sararak ister açık bir şekilde böyle bir yardımcı işlevi tanımlamak harto yaptığı veya böyle bir anonim işlevini kullanın Bjorn yaptı:

funcs = {};
function outer(i) {              // function outer's scope contains 'i'
  return function inner() {      // function inner, closure created
   console.log("My value: " + i);
  };
}
for (var i = 0; i < 3; i++) {
  funcs[i] = outer(i);
}
console.log(window.i)          // print 3 still

Ne funcszaman idam, şimdi kapsam zinciri olacak function inner -> function outer. Bu süre i, for döngüsünde 3 kez yürütülen dış işlevin kapsamında bulunabilir, her seferinde idoğru değer bağlanır. window.iİç yürütüldüğünde değerini kullanmaz .

Daha ayrıntı bulunabilir burada
biz kapatılması ve performans dikkate neden ihtiyaç yanı sıra, burada ne var olarak döngü içinde kapatılmasını yaratmada ortak hata içerir.


Bu kod örneğini nadiren gerçek olarak yazıyoruz, ancak bence temel olanı anlamak için iyi bir örnek. Kapsamı aklımızda tuttuktan ve nasıl zincirlediklerini öğrendikten sonra, Array.prototype.forEach(function callback(el) {})doğal olarak nasıl 'modern' yolların çalıştığını görmek daha açıktır : Doğal olarak geçen geri arama, her bir yinelemede doğru bir şekilde bağlanan sarma kapsamını oluşturur forEach. Böylece, geri el
aramada

13

ES6 blok seviyesi kapsamının yeni özellikleri ile yönetilir:

var funcs = [];
for (let i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
}
for (let j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

OP'nin sorusundaki kod letyerine var.


constaynı sonucu verir ve bir değişkenin değeri değişmeyecekse kullanılmalıdır. Ancak, constfor döngüsünün başlatıcısının içinde kullanımı Firefox'ta yanlış uygulanmıştır ve henüz düzeltilmemiştir. Bloğun içinde bildirilmek yerine, bloğun dışında bildirilir, bu da değişkene bir yeniden bildirimle sonuçlanır ve bu da bir hataya neden olur. letBaşlatıcı iç kısmının kullanımı Firefox'ta doğru bir şekilde uygulandığından, endişelenmenize gerek yok.

10

Hiç kimse henüz forEachyerel değişkenleri kullanarak (yeniden) daha iyi önlemek için işlevini kullanarak önerdi şaşırttı . Aslında, for(var i ...)artık bu nedenle kullanmıyorum .

[0,2,3].forEach(function(i){ console.log('My value:', i); });
// My value: 0
// My value: 2
// My value: 3

// forEachharita yerine kullanmak üzere düzenlendi .


3
.forEach()aslında bir şey eşlemiyorsanız çok daha iyi bir seçenektir.
JLRishe

Bu soru bir dizi üzerinden döngü ile ilgili değil
jherax

Bir dizi fonksiyon oluşturmak istiyor, bu örnek küresel bir değişkeni içermeden bunun nasıl yapılacağını gösteriyor.
Christian Landgren

9

Bu soru gerçekten JavaScript'in geçmişini gösteriyor! Artık Nesne yöntemlerini kullanarak ok işlevleriyle blok kapsamayı engelleyebilir ve döngüleri doğrudan DOM düğümlerinden işleyebiliriz.

const funcs = [1, 2, 3].map(i => () => console.log(i));
funcs.map(fn => fn())

const buttons = document.getElementsByTagName("button");
Object
  .keys(buttons)
  .map(i => buttons[i].addEventListener('click', () => console.log(i)));
<button>0</button><br>
<button>1</button><br>
<button>2</button>


8

Orijinal örneğinizin çalışmamasının nedeni, döngüde oluşturduğunuz tüm kapakların aynı çerçeveye başvurmasıdır. Aslında, tek bir ideğişkene sahip bir nesne üzerinde 3 yöntem olması . Hepsi aynı değeri yazdırdı.


8

Her şeyden önce, bu kodda neyin yanlış olduğunu anlayın:

var funcs = [];
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: " + i); // each should log its value.
    };
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Burada funcs[]dizi başlatılırken, iartırılırken funcsdizi başlatılır ve funcdizi boyutu 3 olur i = 3,. Şimdi funcs[j]()çağrıldığında, i3'e artırılmış olan değişkeni tekrar kullanıyor .

Şimdi bunu çözmek için birçok seçeneğimiz var. Aşağıda bunlardan ikisi bulunmaktadır:

  1. Biz başlatabilir iile letveya yeni bir değişkeni başlatmak indexile letve karşı eşit hale i. Böylece çağrı yapıldığında, indexkullanılır ve başlatma işleminden sonra kapsamı sona erer. Ve arama için indextekrar başlatılacak:

    var funcs = [];
    for (var i = 0; i < 3; i++) {          
        let index = i;
        funcs[i] = function() {            
            console.log("My value: " + index); 
        };
    }
    for (var j = 0; j < 3; j++) {
        funcs[j]();                        
    }
  2. Diğer Seçenek tempFuncasıl işlevi döndüren bir tanıtmak olabilir :

    var funcs = [];
    function tempFunc(i){
        return function(){
            console.log("My value: " + i);
        };
    }
    for (var i = 0; i < 3; i++) {  
        funcs[i] = tempFunc(i);                                     
    }
    for (var j = 0; j < 3; j++) {
        funcs[j]();                        
    }

8

Kapatma yapısı kullanın , bu döngü için ekstra azaltacaktır. Döngü için tek bir işlem yapabilirsiniz:

var funcs = [];
for (var i = 0; i < 3; i++) {     
  (funcs[i] = function() {         
    console.log("My value: " + i); 
  })(i);
}

7

Beyan ettiğinizde varve let birer birer gerçekten ne olduğunu kontrol edeceğiz .

Case1 : kullanmavar

<script>
   var funcs = [];
   for (var i = 0; i < 3; i++) {
     funcs[i] = function () {
        debugger;
        console.log("My value: " + i);
     };
   }
   console.log(funcs);
</script>

Şimdi F12 tuşuna basarak krom konsol pencerenizi açın ve sayfayı yenileyin. Dizinin içindeki her 3 işlevi de harcamayın. Adlı bir özellik göreceksiniz . Adlı bir dizi nesnesi göreceksiniz , bunu genişletin. 3 değerine sahip nesneye bildirilen bir özellik bulacaksınız .[[Scopes]]"Global"'i'

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Sonuç:

  1. Bir 'var'işlevi dışarıda kullanarak bir değişken bildirdiğinizde , bu değişken global değişken olur (yazarak iveya window.ikonsol penceresinde kontrol edebilirsiniz. 3 döndürür).
  2. Bildirdiğiniz annominous fonksiyon, fonksiyonları çağırmazsanız fonksiyonun içindeki değeri çağırmaz ve kontrol etmez.
  3. İşlevi çağırdığınızda, console.log("My value: " + i)değeri Globalnesnesinden alır ve sonucu görüntüler.

CASE2: let kullanma

Şimdi değiştirmek 'var'ile'let'

<script>
    var funcs = [];
    for (let i = 0; i < 3; i++) {
        funcs[i] = function () {
           debugger;
           console.log("My value: " + i);
        };
    }
    console.log(funcs);
</script>

Aynı şeyi yapın, kapsamlara gidin. Şimdi iki nesne göreceksiniz "Block"ve "Global". Şimdi Blocknesneyi genişletin , orada 'i' nin tanımlandığını göreceksiniz ve garip olan şey, her fonksiyon iiçin farklı ise (0, 1, 2) değerdir .

resim açıklamasını buraya girin

Sonuç:

Kullandığınız değişken bildirdiğinizde 'let'bile dışarıda fonksiyonu ancak döngü içinde bu değişken bir küresel değişken olmayacak, bu bir hale gelecektir Block, biz değerini alıyorsanız nedenidir only.That aynı işlev için kullanılabilir düzey değişkeni ifarklı fonksiyonları çağırdığımızda her fonksiyon için.

Ne kadar yakın çalıştığı hakkında daha fazla ayrıntı için, lütfen harika video eğiticisine göz atın https://youtu.be/71AtaJpJHw0


4

Query-js (*) gibi veri listeleri için bir bildirim modülü kullanabilirsiniz . Bu durumlarda kişisel olarak daha az şaşırtıcı bir deklaratif yaklaşım buluyorum

var funcs = Query.range(0,3).each(function(i){
     return  function() {
        console.log("My value: " + i);
    };
});

Daha sonra ikinci döngünüzü kullanabilir ve beklenen sonucu elde edebilirsiniz veya

funcs.iterate(function(f){ f(); });

(*) Sorgu-j'lerin yazarıyım ve bu nedenle onu kullanmak için önyargılıyım, bu yüzden sözlerimi sadece bildirici yaklaşım için söz konusu kütüphane için bir öneri olarak almayın :)


1
Aşağı oylamanın bir açıklamasını isterim. Kod eldeki sorunu çözer. Kodun potansiyel olarak nasıl geliştirileceğini bilmek değerli olacaktır
Rune FS

1
Nedir Query.range(0,3)? Bu, bu soru için etiketlerin bir parçası değil. Ayrıca, üçüncü taraf bir kütüphane kullanıyorsanız, belgelerin bağlantısını sağlayabilirsiniz.
jherax

1
@jherax bunlar ya da elbette bariz gelişmeler. Yorum için teşekkürler. Zaten bir bağlantı olduğunu yemin edebilirdim. İle yazı oldukça anlamsız sanırım :). Bunu dışarıda tutma konusundaki ilk fikrim, kendi kütüphanemin kullanımını zorlamak değil, daha çok beyan edici fikri zorlamaktı. Ancak hinsight bağlantı tamamen orada olması gerektiğini kabul ediyorum
Rune FS

4

Ben forEachsahte bir aralık oluşturma ile kendi kapanış olan işlevi kullanmayı tercih ederim :

var funcs = [];

new Array(3).fill(0).forEach(function (_, i) { // creating a range
    funcs[i] = function() {            
        // now i is safely incapsulated 
        console.log("My value: " + i);
    };
});

for (var j = 0; j < 3; j++) {
    funcs[j](); // 0, 1, 2
}

Bu, diğer dillerdeki aralıklardan daha çirkin görünüyor, ancak IMHO diğer çözümlerden daha az korkunç.


Neye tercih ediyorsun? Bu, başka bir cevaba cevap olarak bir yorum gibi görünüyor. Asıl soruya hiç değinmiyor (daha sonra, herhangi bir yerde çağrılacak bir işlev atamadığınız için).
Quentin

Tam olarak bahsedilen sorunla ilgili: kapatma sorunları olmadan güvenli bir şekilde tekrarlama
Rax Wunter

Şimdi kabul edilen cevaptan çok farklı görünmüyor.
Quentin

Hayır sorunu iyi ve pratik bir şekilde
Rax Wunter

@Quentin Eksileri kullanmadan önce çözümü araştırmanızı tavsiye ederim
Rax Wunter

4

Ve yine başka bir çözüm: başka bir döngü oluşturmak yerine, sadece thisreturn işlevine bağlayın.

var funcs = [];

function createFunc(i) {
  return function() {
    console.log('My value: ' + i); //log value of i.
  }.call(this);
}

for (var i = 1; i <= 5; i++) {  //5 functions
  funcs[i] = createFunc(i);     // call createFunc() i=5 times
}

Bağlanarak bu , sorunu çözer de.


3

Birçok çözüm doğru görünüyor, ancak bu Curryinggibi durumlar için fonksiyonel bir programlama tasarım deseni olarak adlandırıldığından bahsetmiyorlar . Tarayıcıya bağlı olarak bağdan 3-10 kat daha hızlı.

var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = curryShowValue(i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

function curryShowValue(i) {
  return function showValue() {
    console.log("My value: " + i);
  }
}

Farklı tarayıcılarda performans kazancını görün .


@TinyGiant Döndürülen işleve sahip örnek hala performans için en iyi duruma getirilmiş curried. Tüm JavaScript blogcuları gibi ok işlevleri bandwagonuna atlamam. Havalı ve temiz görünürler, ancak önceden tanımlanmış işlevleri kullanmak yerine yazma işlevlerini yerinde desteklerler. Bu sıcak yerlerde bariz bir tuzak olabilir. Başka bir sorun, sadece sözdizimsel şeker olmamalarıdır, çünkü gereksiz bağlamalar yaparlar ve böylece sarma kapakları oluştururlar.
Pawel

2
Gelecekteki okuyuculara uyarı: Bu cevap Currying terimini yanlış uygular . "Currying, birden fazla argümanı alan bir fonksiyonu, argümanların bir parçası olan bir dizi fonksiyona böldüğünüz zamandır." . Bu kod böyle bir şey yapmaz. Burada yaptığınız tek şey, kabul edilen yanıttan kodu almak, bazı şeyleri hareket ettirmek, stili ve biraz isimlendirmek, daha sonra kategorik olarak olmayan köri olarak adlandırmaktır.

3

Kodunuz çalışmıyor, çünkü yaptığı şey:

Create variable `funcs` and assign it an empty array;  
Loop from 0 up until it is less than 3 and assign it to variable `i`;
    Push to variable `funcs` next function:  
        // Only push (save), but don't execute
        **Write to console current value of variable `i`;**

// First loop has ended, i = 3;

Loop from 0 up until it is less than 3 and assign it to variable `j`;
    Call `j`-th function from variable `funcs`:  
        **Write to console current value of variable `i`;**  
        // Ask yourself NOW! What is the value of i?

Şimdi soru, ifonksiyon çağrıldığında değişkenin değeri nedir? İlk döngü, koşuluyla oluşturulduğundan i < 3, koşul yanlış olduğunda hemen durur, bu yüzden öyledir i = 3.

İşlevlerinizin oluşturulduğu zamanda, kodlarının hiçbirinin yürütülmediğini, yalnızca daha sonra kullanılmak üzere kaydedildiğini anlamalısınız. Ve daha sonra çağrıldıklarında, tercüman onları yürütür ve "Şu andaki değeri nedir i?" Diye sorar.

Yani, amacınız önce iişlevinin değerini kaydetmek ve sonra bundan sonra işlevi kaydetmek funcs. Bu, örneğin şu şekilde yapılabilir:

var funcs = [];
for (var i = 0; i < 3; i++) {          // let's create 3 functions
    funcs[i] = function(x) {            // and store them in funcs
        console.log("My value: " + x); // each should log its value.
    }.bind(null, i);
}
for (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

Bu şekilde, her işlevin kendi değişkeni olacaktır xve bunu her yinelemenin xdeğerine ayarladık i.

Bu, bu sorunu çözmenin çeşitli yollarından sadece biridir.


3
var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = function(param) {          // and store them in funcs
    console.log("My value: " + param); // each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j](j);                      // and now let's run each one to see with j
}

3

Var yerine let (bloke kapsamı) kullanın.

var funcs = [];
for (let i = 0; i < 3; i++) {      
  funcs[i] = function() {          
    console.log("My value: " + i); 
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      
}

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.