43 Soru: Döngüler içinde JavaScript kapatılması - basit pratik örnek

tarafından oluşturulan soru Thu, May 23, 2019 12:00 AM

 
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ı:

  

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

Oysa çıkmasını isterdim:

  

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


Aynı sorun, işlevi çalıştırmadaki gecikmeye olay dinleyicileri neden oluyorsa 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 zaman uyumsuz kod, ör. Promises'i kullanarak:

 
// 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));
}

Bu temel sorunun çözümü nedir?

    
2605
  1. Sayı indeksleri kullanıyorsanız funcs'un dizi olmasını istemediğinizden emin misiniz? Sadece bir kafa yukarı.
    2013-07-26 11: 12: 10Z
  2. Bu gerçekten kafa karıştırıcı bir sorundur. Bu makale, anlamama yardımcı oluyor . Başkalarına da yardımcı olabilir.
    2014-05-03 15: 38: 10Z
  3. Başka bir basit ve açıklanmış çözüm: 1) Yuvalanmış İşlevler "üstlerindeki" kapsamlara erişebilir; 2) bir kapatma çözümü .. "Bir kapatma, ana işlev kapatıldıktan sonra bile ana içeriğe erişimi olan bir işlevdir".
    2014-12-17 01: 22: 21Z
  4. Daha iyi anlamak için bu bağlantıya bakın javascript.info /öğretici /gelişmiş fonksiyonları
    2015-04-02 12: 01: 47Z
  5. ES6 'da, önemsiz bir çözüm i değişkenini let , döngünün gövdesine yöneliktir.
    2016-09-13 20: 34: 13Z
30 Yanıtlar                              30                         

Sorun şu ki, anonim işlevlerinizin her birinde bulunan i değişkeni, işlev dışındaki aynı değişkene bağlı.

Klasik çözüm: Kapaklar

Yapmak istediğiniz, her bir işlevdeki değişkeni, işlev dışındaki farklı, 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’te blok kapsamı olmadığından - yalnızca işlev kapsamı - işlev oluşturma işlevini yeni bir işlevle sararak, "i" değerinin istediğiniz gibi kaldığından emin olursunuz.


2015 Çözüm: forEach

Array.prototype.forEach işlevinin görece yaygın kullanılabilirliği ile (2015'te), öncelikle bir dizi değer üzerinde yinelemeyi içeren bu durumlarda, .forEach()'un her yinelemede farklı bir kapatma elde etmek için temiz ve doğal bir yol sağladığını belirtmek gerekir. Başka bir deyişle, değerler içeren bir dizi diziniz olduğunu varsayalım (DOM referansları, nesneler, ne olursa olsun) ve her öğeye özgü geri aramaları ayarlama sorunu ortaya çıkarsa, bunu yapabilirsiniz:

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

Buradaki fikir, .forEach döngüsüyle kullanılan geri çağırma işlevinin her çağrılmasının kendi kapanması olacağıdır. Bu işleyiciye iletilen parametre, yinelemenin belirli adımına özgü dizi öğesidir. Eşzamansız bir geri aramada kullanılıyorsa, yinelemenin diğer adımlarında oluşturulan diğer geri aramaların hiçbiriyle çarpışmaz.

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


ES6 çözümü: let

ECMAScript 6 (ES6), let tabanlı değişkenlerden farklı olarak kapsamlanan const ve var anahtar sözcükleri sunar. Örneğin, let tabanlı bir dizine sahip bir döngüde, döngüdeki her bir yineleme, i değerinde yeni bir değere sahip olacaktır; burada her değer, döngü içinde kapsamdadır, bu nedenle kodunuz beklediğiniz şekilde çalışır. Pek çok kaynak var, ancak 2ality'nin blok kapsamı yayınını öneriyorum harika bir bilgi kaynağı olarak.

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

Ancak, Edge 14'ten önceki IE9-IE11 ve Edge’in let’u desteklediğini, ancak yukarıdakilerin yanlış olduğunu unutmayın (her seferinde yeni bir i oluşturmazlar, bu nedenle yukarıdaki tüm işlevler bizim kullandığımız gibi 3 günlüğe girer) var). Kenar 14 nihayet doğru alır.

    
2000
2019-05-23 16: 03: 12Z
  1. function createfunc(i) { return function() { console.log("My value: " + i); }; } değişkenini kullandığı için i hala kapanmıyor mu?
    2014-03-28 03: 45: 40Z
  2. Maalesef, bu cevap modası geçmiş ve kimse en altta doğru cevabı görmeyecek - Function.bind() kullanarak şu ana kadar kesinlikle tercih ediliyor, bkz. stackoverflow.com/a/19323214/785541 .
    2014-06-20 12: 21: 23Z
  3. @ Wladimir: .bind()'un "doğru cevap" olduğu yönündeki öneriniz doğru değil. Her birinin kendi yeri var. .bind() ile this değerini bağlamadan bağımsız değişkenleri bağlayamazsınız. Ayrıca, i argümanının bir kopyasını, bazen gerekli olan çağrılar arasında değiştirebilme yeteneği olmadan elde edersiniz. Bu yüzden onlar oldukça farklı yapılardır, .bind() uygulamalarının tarihsel olarak yavaş olduğunu söylemeye gerek yok. Basit örnekte elbette ya işe yarayacaktı, ama kapanışlar anlaşılması gereken önemli bir kavram ve sorunun konusu da buydu.
    2014-07-12 02: 35: 51Z
  4. Lütfen bu geri dönüş işlevi kesmelerini kullanmayı bırakın, bunun yerine [] .forEach veya [] .map komutlarını kullanın, çünkü aynı kapsam değişkenlerini tekrar kullanmaktan kaçınırlar.
    2015-02-07 10: 23: 16Z
  5. @ ChristianLandgren: Bu yalnızca bir Array yineleme yapıyorsanız kullanışlıdır. Bu teknikler "kesmek" değildir. Onlar temel bilgiler.
    2015-06-29 16: 31: 47Z

Dene:

 
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 bence @ Aust’ın .bind’u kullanma konusundaki daha yeni yanıtını düşünüyorum. Ayrıca _.partial’un bind’u ile uğraşmak istemediğiniz veya uğraşmak istemediğiniz zaman thisArg’da bir çizgi /alt çizgi vardır.

    
362
2019-05-22 11: 33: 23Z
  1. }(i)); ile ilgili herhangi bir açıklama?
    2018-04-06 01: 32: 18Z
  2. @ aswzen Fonksiyona i argümanı olarak index ilettiğini düşünüyorum.
    2018-07-26 22: 01: 50Z
  3. aslında yerel değişken dizini yaratıyor.
    2019-03-15 15: 17: 08Z

Henüz belirtilmemiş bir başka yol, 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 tarafından belirtildiği gibi, önce ilmekin dışında bir işlev oluşturarak ve ardından ilmek 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]();
}
    
334
2018-02-21 16: 03: 26Z
  1. Bu günlerde de aynı şeyi yapıyorum, ayrıca lo-dash /underscore's _.partial'u da seviyorum.
    2014-12-08 05: 18: 31Z
  2. .bind(), ECMAScript 6 özellikleriyle büyük ölçüde eski olacak. Ayrıca, bu aslında her yineleme için iki fonksiyon oluşturur. Önce isimsiz, sonra .bind() tarafından üretilen. Daha iyi kullanım, onu döngünün dışında oluşturmak, sonra içinde .bind() kullanmak olacaktır.
    2015-06-28 03: 29: 07Z
  3. @ squint @mekdev - Siz ikiniz de haklısınız. İlk örneğim, bind'un nasıl kullanıldığını göstermek için hızlı bir şekilde yazıldı. Önerilerinize göre başka bir örnek ekledim.
    2015-06-29 16: 23: 27Z
  4. İki O (n) döngüsünün üzerinde hesaplama israf etmek yerine, sadece (var i = 0; i < 3; i ++) {log.call ( bu benim); }
    2015-09-11 12: 14: 20Z
  5. . bind (), kabul edilen cevabın PLUS’un this ile ilgisini çektiğini söylüyor.
    2017-01-08 05: 55: 07Z

Bir Anında Çağrılan İşlev İfadesi kullanarak, en basit ve en okunaklı yolu kullanma bir dizin değişkenini dahil et:

 
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, i yineleyiciyi index olarak tanımladığımız isimsiz fonksiyona gönderir. Bu, i değişkeninin IIFE içindeki zaman uyumsuz işlevlerde daha sonra kullanılmak üzere saklandığı bir kapanma yaratır.

    
255
2019-05-22 11: 32: 46Z
  1. Daha fazla kod okunabilmesi ve hangi i'un ne olduğuna ilişkin karışıklığı önlemek için, işlev parametresini index olarak yeniden adlandırırım.
    2014-01-10 16: 45: 53Z
  2. Bu tekniği, orijinal soruda açıklanan funcs dizisini tanımlamak için nasıl kullanırsınız?
    2014-11-30 13: 17: 19Z
  3. @ Nico index yerine i kullanmanız dışında, orijinal sorudakiyle aynı şekilde.
    2015-03-31 20: 54: 35Z
  4. @ JLRishe var 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](); }
    2015-04-01 09: 22: 44Z
  5. @ Nico OP'in özel durumunda, sadece sayılar üzerinde yineliyorlar, bu yüzden bu .forEach() için çok iyi bir durum olmayacak, ancak çoğu zaman biri bir dizi ile başlıyor, forEach() iyi bir seçimdir, gibi: var nums [4, 6, 7]; var funcs = {}; nums.forEach(function (num, i) { funcs[i] = function () { console.log(num); }; });
    2015-04-01 10: 05: 13Z

Partiye biraz geç kaldım, ancak bugün bu konuyu araştırıyordum ve cevapların çoğunun Javascript’in kapsamları nasıl ele aldığını tam olarak ele almadığını fark ettim.

Pek çokdiğerleri belirtilen, problem, iç fonksiyonun aynı i değişkeni referans göstermesidir. Öyleyse neden her yinelemede yeni bir yerel değişken oluşturmuyoruz ve bunun yerine iç işlev referansına sahip değiliz?

 
//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]();
}

Tıpkı daha önce olduğu gibi, her bir iç işlev i'a atanan son değeri verdiğinde, şimdi her iç işlev ilocal'a atanmış son değeri verir. Ancak her yinelemenin kendine ait bir ilocal değeri olmamalı mı?

Anlaşıldı, sorun bu. Her yineleme aynı kapsamı paylaşıyor, bu yüzden birinciden sonraki her yineleme ilocal'un üzerine yazıyor. MDN 'den:

  

Önemli: JavaScript'in blok kapsamı yok. Bir bloğun getirdiği değişkenler, içerme işlevine veya komut dosyasına dahil edilir ve bunları ayarlamanın etkileri bloğun ötesinde kalır. Başka bir deyişle, blok ifadeleri kapsam içermez. "Bağımsız" bloklar geçerli bir sözdizimi olmasına rağmen, JavaScript’te bağımsız bloklar kullanmak istemezsiniz, çünkü C veya Java’da bu tür bloklar gibi bir şey yaptıklarını düşünüyorsanız, yaptıkları şeyi yapmazlar.

Vurgunun tekrarı:

  

JavaScript'in blok kapsamı yok. Bir bloğun getirdiği değişkenler, içerme işlevine veya komut dosyasına dahil edilir

Bunu, her yinelemede bildirmeden önce ilocal'u 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;
}

Bu tam olarak bu hatanın bu kadar zor olmasının nedeni budur. Bir değişkeni yeniden bildirseniz bile, Javascript bir hata atmaz ve JSLint bir uyarı bile atmaz. Bu aynı zamanda, bunu çözmenin en iyi yolunun, Javascript'te iç fonksiyonların dış değişkenlere erişimi olduğu fikri olan kapamalardan yararlanmaktır, çünkü iç kapsamlar, dış kapsamları "kapsamlar".

Kapaklar

Bu aynı zamanda, iç işlevlerin dış değişkenleri "beklettiği" ve dış işlev döndüğü halde onları canlı tuttuğu anlamına gelir. Bunu kullanmak için, yalnızca yeni bir kapsam oluşturmak, yeni kapsamda ilocal ilan etmek ve ilocal kullanan bir iç işlev döndürmek için bir sarmalayıcı işlevi oluşturur ve çağırırız (aşağıda daha fazla açıklama):

 
//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şlevi yalnızca erişebileceği özel bir ortam, "kapatma" sağlar. Böylece, sarıcı işlevini her çağırdığımızda, ilocal değişkenlerinin birbiriyle çarpışmamasını ve üzerine yazmamasını sağlamak için kendi ayrı ortamıyla yeni bir iç işlev oluştururuz. Birkaç küçük optimizasyon, diğer birçok SO kullanıcısının verdiği son 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üncelle

Şimdi ana akım ES6 ile, blok kapsamındaki değişkenleri oluşturmak için yeni let anahtar sözcüğünü 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]();
}

Şimdi ne kadar kolay olduğuna bak! Daha fazla bilgi için bilgilerimin dayanmadığı bu cevaba bakın.

    
151
2018-03-01 22: 43: 11Z
  1. IIFE'yi de nasıl açıkladığınızı beğendim. Onu arıyordum. Teşekkürler.
    2017-10-26 21: 45: 21Z
  2. Artık, JavaScript'te let ve const anahtar sözcüklerini kullanarak blok kapsamı gibi bir şey var. Bu cevap bunu içerecek şekilde genişletildiyse, bence küresel olarak çok daha yararlı olurdu.
    2017-12-27 03: 12: 06Z
  3. @ TinyGiant kesin bir şey, let hakkında bir miktar bilgi ekledim ve daha ayrıntılı bir açıklama ekledim
    2018-03-01 22: 44: 09Z
  4. @ woojoo666 Yanıtınız, şöyle bir döngüde iki alternatif URL’yi çağırmak için de işe yarayabilir mi: 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++ }? (window.open () işlevini getelementbyid ile değiştirebilir ......)
    2018-05-14 19: 08: 24Z
  5. @ nuttyaboutnatty böyle geç bir cevap için özür dilerim. Örnekteki kod zaten çalışıyor gibi görünmüyor. Zaman aşımı işlevlerinizde i kullanmıyorsunuz, bu nedenle bir kapanmaya ihtiyacınız yok
    2018-06-03 22: 58: 23Z

ES6 artık yaygın olarak destekleniyorsa, bu sorunun en iyi yanıtı değişti. ES6, bu kesin durum için let ve const anahtar sözcükleri sağlar. Kapaklarla uğraşmak yerine, let'u şöyle bir 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); 
    };
}

val daha sonra döngünün o özel dönüşüne özgü olan bir nesneye 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 kolaylaştırıyor.

const, değişken atamasının ilk atamadan sonra yeni bir referansa geri döndürülemeyeceği ek kısıtlama ile let'a benzer.

Tarayıcı desteği, en yeni tarayıcı sürümlerini hedefleyenler için şimdi burada. const/let en son Firefox, Safari, Edge ve Chrome'da desteklenmektedir. Ayrıca Düğümde desteklenir ve Babel gibi derleme araçlarından yararlanarak her yerde kullanabilirsiniz. Burada çalışan bir örnek görebilirsiniz: http://jsfiddle.net/ben336/rbU4t/2/

Buradaki dokümanlar:

Ancak, Edge 14'ten önceki IE9-IE11 ve Edge’in let’u desteklediğini, ancak yukarıdakilerin yanlış olduğunu unutmayın (her seferinde yeni bir i oluşturmazlar, bu nedenle yukarıdaki tüm işlevler bizim kullandığımız gibi 3 günlüğe girer) var). Kenar 14 nihayet doğru alır.

    
139
2019-05-22 11: 38: 58Z
  1. Ne yazık ki, 'let' özellikle mobilde hala tam olarak desteklenmiyor. developer.mozilla.org/en-US/docs/Web /JavaScript /Referans /...
    2016-02-23 17: 47: 32Z
  2. Haziran '16'dan itibaren let iOS Safari, Opera Mini ve Safari 9 hariç tüm büyük tarayıcı sürümlerinde desteklenir. Her zaman yeşil olan tarayıcılar bunu destekler. Babil, yüksek uyumluluk modu açık olmadan beklenen davranışı sürdürmek için doğru şekilde aktarır.
    2016-06-22 10: 18: 14Z
  3. @ DanPantry bir güncelleme için zaman hakkında evet :) Const, doc linkleri ve daha iyi uyumluluk bilgileri de dahil olmak üzere, mevcut durumları daha iyi yansıtacak şekilde güncellendi.
    2016-06-27 14: 24: 27Z
  4. Bu yüzden babil'i kodumuzu değiştirmek için kullanmıyoruz, böylece ES6 /7'yi desteklemeyen tarayıcılar neler olduğunu anlayabilir mi?
    2018-03-19 15: 56: 00Z

Bunu söylemenin bir başka yolu, işlevinizdeki i'un işlevi oluşturma zamanında değil, işlevi yürütürken bağlanmış olmasıdır.

Kapatma oluşturduğunuzda, i, kapama oluşturduğunuzdaki gibi bir kopyasını değil, dış kapsamda tanımlanan değişkene bir referanstır. Yürütme 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 çalışmanın yollarını sunar.

Sadece netlik için bir açıklama ekleyeceğimi düşündüm. Bir çözüm için, şahsen, Harto’yla birlikte giderdim.buradaki cevaplardan bunu yapmanın kendi kendini açıklayıcı yolu. Gönderilen kodlardan herhangi biri işe yarayacak, ancak neden yeni bir değişken (Freddy ve 1800'ler) tanımladığımı veya garip gömülü kapama sözdizimini (apphacker) açıkladığımı açıklamak için bir kapatma fabrikası seçmeyi tercih ediyorum.

    
84
2017-12-26 11: 07: 51Z

Anlamanız gereken şey, javascript'teki değişkenlerin kapsamı fonksiyona dayanır. Bu, blok kapsamına sahip olduğunuz c # deyiminden önemli bir farktır ve değişkeni for for içindeki birine kopyalamak işe yarayacaktır.

Değişken şimdi işlev kapsamına sahip olduğundan, apphacker'ın yanıtı gibi işlevi döndürmeyi değerlendiren bir işlevde kaydırma işlemi hile yapar.

Ayrıca blok kapsam kuralının kullanılmasına izin verecek var yerine bir let anahtar sözcüğü vardır. Bu durumda for içindeki bir değişkeni 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]();
}
    
67
2019-05-22 11: 16: 54Z
  1. @ nickf hangi tarayıcı? Dediğim gibi, uyumluluk sorunları var, yani IE’de desteklendiğini sanmıyorum gibi ciddi uyumluluk sorunları demek istiyorum.
    2009-04-15 06: 54: 50Z
  2. @ nickf evet, bu referansı kontrol edin: developer.mozilla.org/En/New_in_JavaScript_1.7 ... izin tanımları bölümünü kontrol edin, bir döngü içinde açık bir örnek vardır
    2009-04-16 02: 55: 23Z
  3. @ nickf hmm, aslında sürümü açıkça belirtmeniz gerekir: < script type = "application /javascript; version = 1.7" /> ... IE kısıtlaması nedeniyle aslında hiçbir yerde kullanmadım, pratik değil :(
    2009-04-16 02: 58: 54Z
  4. 2013-01-15 10: 21: 25Z
  5. İzin kullan sonra kullan
    2013-10-14 17: 13: 31Z

Teknikte, parametre olarak geçirmeden ziyade değişken değeri atamanıza izin veren Bjorn's (apphacker) 'a benzeyen başka bir varyasyon var:

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

Kullandığınız teknik ne olursa olsun, index değişkeninin, iç işlevinin döndürülen kopyasına bağlı bir tür statik değişken haline geldiğini unutmayın. Yani, değerlerinde yapılan değişiklikler aramalar arasında korunur. Çok kullanışlı olabilir.

    
55
2019-05-22 11: 28: 49Z
  1. Teşekkürler ve çözümünüz çalışıyor. Fakat bunun neden işe yaradığını sormak istiyorum, ancak var hattını ve return hattını değiştirmek işe yaramaz mı? Teşekkürler!
    2013-12-03 02: 56: 23Z
  2. @ midnite var ve return'u değiştirirseniz, değişken iç işlevi döndürmeden önce atanmaz.
    2013-12-03 04: 35: 28Z

Bu, JavaScript'teki kapanışları kullanmadaki yaygın hatayı açıklar.

Bir işlev yeni bir ortam tanımlar

düşünün:

 
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

makeCounter her çalıştırıldığında, {counter: 0} yeni bir nesnenin oluşturulmasına neden olur. Ayrıca, obj'un yeni bir kopyası Yeni nesneye referans vermek için de yaratılmıştır. Dolayısıyla, counter1 ve counter2 birbirinden bağımsızdır.

Döngüdeki kapanışlar

Bir döngüde kapatmanın kullanılması zordur.

Bir düşünün:

 
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

counters[0] ve counters[1]’un bağımsız olmadığına dikkat edin. Aslında, aynı obj'da çalışıyorlar!

Bunun nedeni, belki de performans nedenleriyle, döngünün tüm yinelemelerinde paylaşılan obj'un yalnızca bir kopyasının olmasıdır. Her yinelemede {counter: 0} yeni bir nesne oluştursa da, obj’un aynı kopyası yalnızca en yeni nesneye referans.

Çö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);
  }
}

Bu, doğrudan işlev kapsamındaki yerel değişkenlerin yanı sıra işlev bağımsız değişkenlerinin ayrıldığı için çalışır girişte yeni kopyalar.

Ayrıntılı bir tartışma için lütfen JavaScript kapatma tuzakları ve kullanımı

'na bakın.     
51
2013-04-20 09: 59: 57Z

En basit çözüm,

olacaktır.

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" yi uyarır. Bunun nedeni, döngü için oluşturulan anonim işlevlerin aynı kapanışı paylaşması ve bu kapanışta i'un değerinin aynı 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 ile kaplamaktır. > (Hemen Çağrılan İşlev İfadesi) ve parametre olarak new_i'u geçme ve i olarak yakalama. Anonim işlev hemen yürütüldüğünden, anonim işlev içinde tanımlanan her işlev için i değeri farklıdır.

Bu çözüm, bu sorunla karşılaşan orijinal kodda minimum değişiklik yapılması gerekeceğinden, bu tür herhangi bir soruna uyacak gibi görünüyor. Aslında, bu tasarım gereği, hiç bir sorun olmamalı!

    
48
2017-12-28 08: 07: 40Z
  1. Bir kitapta benzer bir şey okudum. Ben de bunu tercih ediyorum, çünkü mevcut kodunuza dokunmak zorunda değilsiniz (bu kadar) ve kendi kendine çağıran fonksiyon kalıbını öğrendikten sonra neden yaptığınız belli oluyor: yeni yaratılan bu değişkeni yakalamak kapsamı.
    2013-07-26 11: 18: 39Z
  2. @ DanMan Teşekkürler. Kendi kendine çağıran anonim işlevler, javascript'in blok düzeyi değişken kapsamı eksikliğini gidermenin çok iyi bir yoludur.
    2013-07-26 12: 20: 44Z
  3. Kendi kendine arama veya kendi kendine arama bu teknik için uygun bir terim değil, IIFE (Hemen Çağrılan İşlev İfadesi) daha doğru . Ref: benalman.com/news/2010/11/…
    2015-10-27 04: 29: 35Z

bu kısa olanı deneyin

  • dizi yok

  • döngü için fazladan boşluk yok


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

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

http://jsfiddle.net/7P6EN/

    
30
2013-09-19 14: 20: 24Z
  1. Çözümünüz doğru çıktı gibi görünüyor ancak gereksiz bir şekilde işlevler kullanıyor, neden çıktıda konsol değil? Asıl soru, aynı kapanışa sahip anonim işlevlerin oluşturulması ile ilgilidir. Sorun şu ki, tek bir kapanışları olduğu için, i değeri her biri için aynı. Umarım almışsındır.
    2015-06-28 08: 51: 57Z

OP tarafından gösterilen kodla ilgili ana sorun, i'un 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.
};

funcs[someIndex], () yürütülene kadar hata oluşmaz. Bu aynı mantığı kullanarak, i'un değerinin de bu noktaya kadar toplanmadığı açık olmalıdır. Orijinal döngü bittiğinde, i++, i'u 3 değerine getirir ve bu, i < 3'un başarısız olmasına ve döngü sona ermesine neden olur. Bu noktada, i, 3 ve funcs[someIndex]() kullanıldığında ve i değerlendirildiğinde, her seferinde 3'tür.

Bunu aşmak için i'u karşılaştığı gibi değerlendirmelisiniz. Bunun zaten funcs[i] (3 benzersiz dizin olduğu) şeklinde olduğunu unutmayın. Bu değeri yakalamanın birkaç yolu vardır. Bunlardan biri, burada zaten gösterilen birkaç şekilde gösterilen bir işleve parametre olarak geçirmektir.

Başka bir seçenek de değişken üzerinde kapanabilecek bir fonksiyon 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); 
    };
};
    
26
2014-03-05 23: 03: 24Z

İşte forEach kullanan basit bir çözüm (IE9’a geri dönüyor):

 
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ı:

 
My value: 0
My value: 1
My value: 2
    
25
2019-06-10 11: 56: 55Z
  1. forEach IE8 veya daha düşük bir sürümü desteklemiyor !!!
    2018-01-16 13: 16: 44Z
  2. Bu yüzden "IE9'a geri dönüyor" dedim .... ....
    2018-01-17 23: 38: 38Z

JavaScript, bildirildikten sonra erişebildikleri kapsamı "kapatır" ve bu kapsam değiştiğinde değişkenler olsa 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ın üzerine kapanır (global, bunun nedeni, bunlar bildirildikleri kapsam olduğu için).

Daha sonra bu fonksiyonlar, global kapsamdaki en güncel i değerini kaydederek çağrılır. Bu kapanmanın sihri ve hüsranı.

"JavaScript, bildirildiği kapsamın üzerine yaklaşıyor ve bu kapsam değişikliği içindeki değişken değerler olsa bile bu kapsamın erişimini koruyor."

let yerine var kullanmak, for döngüsünün her çalışması sırasında yeni bir kapsam oluşturarak çözer ve her bir fonksiyonun kapanması için ayrı bir kapsam oluşturur. Başka çeşitli teknikler de aynı şeyi ekstra fonksiyonlarla 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]()
}

(let değişkenleri blok kapsamlandırır. Bloklar küme parantezleriyle belirtilir, ancak for döngüsü durumunda, başlangıç ​​değişkeninin i bizim durumumuzda parantez içinde bildirildiği kabul edilir.)

    
22
2019-03-21 16: 35: 08Z
  1. Bu cevabı okuyana kadar bu kavramı anlamakta zorlandım. Gerçekten önemli bir noktaya değiniyor - i'un değeri küresel kapsamda belirleniyor. for döngüsünün çalışması bittiğinde, i'un genel değeri şimdi 3'tür. Bu nedenle, bu işlev dizide çalıştırıldığında (funcs[j] diyelim), bu işlevdeki i, genel i değişkenine (3'tür) başvuruyor.
    2017-04-05 02: 50: 29Z

ES6 blok düzeyinde kapsam belirlemenin 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, let var yerine .

    
14
2017-12-26 20: 27: 59Z
  1. const aynı sonucu verir ve bir değişkenin değeri değişmediğinde kullanılmalıdır. Ancak, for loop'un başlatıcısında const'un kullanılması Firefox'ta yanlış bir şekilde uygulanmış ve henüz düzeltilmemiştir. Bloğun içinde bildirilmek yerine, bloğun dışında ilan edilir ve bu da değişkene yeniden yapılanma ile sonuçlanır ve bu da bir hatayla sonuçlanır. Başlatıcının içinde let'un kullanılması Firefox'ta doğru bir şekilde uygulanmıştır, bu nedenle orada endişelenmenize gerek yoktur.
    2017-12-27 03: 05: 48Z

Çeşitli çözümler okuduktan sonra, bu çözümlerin çalışma nedeninin kapsam zinciri kavramına bağlı olduğunu eklemek isterim. JavaScript, yürütme sırasında bir değişkeni çözme yöntemidir.

  • Her işlev tanımı, tüm yerel ayarları içeren bir kapsam oluşturur. var ve arguments ile bildirilen değişkenler.
  • Başka bir (dış) işlev içinde tanımlanmış iç işlev varsa, bu bir zincir oluşturur ve yürütme sırasında kullanılacak
  • Bir işlev yürütüldüğünde, çalışma zamanı kapsam zincirini arayarak değişkenleri değerlendirir. Eğer bir değişken zincirin belirli bir noktasında bulunursa, onu aramayı ve kullanmayı durduracaktır, aksi takdirde window'a ait olan global kapsam ulaşana kadar devam eder.

İ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

funcs çalıştırıldığında, kapsam zinciri function inner -> global olacaktır. i değişkeni function inner'da bulunamadığı için (ne var kullanılarak bildirilmiş ne de argümanlar olarak geçilmemiştir), i'un değeri en sonunda window.i olan global kapsamda bulunana kadar aramaya devam eder.

Dış bir fonksiyona sararak ya açıkça harto gibi bir yardımcı işlev tanımlayın ya da 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

funcs yürütüldüğünde, şimdi kapsam zinciri function inner -> function outer olacaktır. Bu süre i, dış fonksiyonun for döngüsünde 3 kez yürütülen kapsamda bulunabilir, her seferinde doğru şekilde i bağlanmış değer vardır. İç yürütme sırasında window.i değerini kullanmaz.

Daha fazla ayrıntı bulunabilir burada Bu, döngüde kapama yaratma konusundaki yaygın hatayı burada sahip olduğumuz yanı sıra, kapatmaya ve performans değerlendirmesine neden ihtiyaç duyduğumuzu da içerir.

    
13
2017-05-23 12: 02: 57Z
  1. Bu kod örneğini nadiren gerçek olarak yazıyoruz, ancak temelini anlamak için iyi bir örnek teşkil ettiğini düşünüyorum. Kapsamı ve birlikte nasıl zincirlendiklerini bir kez düşündükten sonra, neden Array.prototype.forEach(function callback(el) {}) gibi diğer 'modern' yolların doğal olarak çalıştığını görmek daha açıktır: Doğal olarak iletilen geri arama, forEach'un her bir yinelemesinde el ile doğru şekilde bağlanmış olan sarma kapsamını oluşturur. Böylece geri aramada tanımlanan her iç işlev doğru el değerini kullanabilir.
    2017-04-26 14: 19: 07Z

Hiç kimse henüz yerel değişkenleri kullanmaktan (yeniden) kaçınmak için forEach işlevini kullanmayı önermedi. Aslında, bu nedenle artık for(var i ...) kullanmıyorum.

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

//harita yerine forEach kullanacak şekilde düzenlendi.

    
9
2017-12-26 19: 51: 50Z
  1. .forEach(), aslında bir şey eşleştirmiyorsanız çok daha iyi bir seçenektir ve Daryl, gönderilmeden 7 ay önce bu konuda şaşırmanız gereken hiçbir şey olmadığını belirtti. div>
    2015-03-31 19: 59: 43Z
  2. Bu soru, bir dizi üzerindeki döngü ile ilgili değildir
    2015-10-27 04: 14: 23Z
  3. Peki, bir işlev dizisi oluşturmak istiyor, bu örnekte global bir değişken içermeden bunun nasıl yapılacağı gösteriliyor.
    2015-11-11 21: 25: 03Z

Bu soru gerçekten JavaScript tarihini gösteriyor! Artık ok işlevleriyle blok kapsamayı engelleyebilir ve Object yöntemlerini kullanarak 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>
    
9
2018-01-13 13: 17: 57Z

Öncelikle, 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[] dizisi başlatılırken, i artırılır, funcs dizisi başlatılır ve func dizisinin boyutu 3 olur, yani i = 3,. Şimdi funcs[j]() çağrıldığında, yine de 3'e artırılmış olan i değişkenini kullanıyor.

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

  1. i ile let'u başlatabilir veya index ile let ile yeni bir değişken başlatabilir ve i'a eşitleyebiliriz. Böylece çağrı yapılırken index kullanılacak ve başlatılmasından sonra kapsamı bitecek. Ve arama için index tekrar 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, asıl işlevi döndüren bir tempFunc 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
2017-12-26 22: 46: 45Z

Orijinal örneğinizin çalışmamasının nedeni, döngüde oluşturduğunuz tüm kapakların aynı çerçeveye referans vermesidir. Aslında, tek bir i değişkeni olan bir nesnede 3 yöntem vardır. Hepsi aynı değeri bastırdı.

    
7
2017-12-26 20: 27: 18Z
  

var ve let’u bildirdiğinizde gerçekte ne olacağını kontrol edeceğiz   birer birer.

Örnek 1 : var kullanarak

 
<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 'ye basarak krom konsol pencerenizi açın ve sayfayı yenileyin. Dizinin içindeki her 3 işlevi de genişletin. [[Scopes]] adında bir özellik göreceksiniz. Bunu genişletin. Birini göreceksin "Global" adındaki dizi nesnesi, bunu genişletir. 'i' numaralı mülkü, değeri 3 olan nesneye bulacaksınız.

 buraya resim açıklamasını girin

 buraya resim açıklamasını girin

Sonuç:

  1. Bir işlevin dışında 'var' kullanarak bir değişken bildirdiğinizde, genel değişken haline gelir (i yazarak denetleyebilirsiniz veya  window.i konsol penceresinde. 3) döndürür.
  2. Bildirdiğiniz annominous fonksiyonu, siz çağırmazsanız, fonksiyonun içindeki değeri çağırmaz ve kontrol etmez. fonksiyonlar.
  3. İşlevi çağırdığınızda, console.log("My value: " + i) değeri Global nesnesinden alır ve sonucu.

CASE2: let kullanarak

Şimdi 'var''u 'let' ile değiştirin

 
<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 nesneyi "Block" ve "Global" göreceksiniz. Şimdi Block nesneyi genişlet 'i' nin orada tanımlandığını göreceksiniz ve garip olan şey, her işlev için i'un farklı olması (0, 1, 2) değerinin olmasıdır.

 buraya resim açıklamasını girin

Sonuç:

İşlev dışında bile 'let' kullanarak değişken bildirirseniz, ancak döngü içinde bu değişken Global olmaz değişkeni, yalnızca aynı işlev için kullanılabilen Block düzey değişkeni haline gelecektir. işlevleri çağırdığımızda her işlev için i farklı değer alıyoruz.

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

    
7
2018-01-16 14: 29: 57Z

kapanma yapısını kullanın, bu işlem ekstralarınızı düşürür döngü için. Bunu döngü için tek olarak yapabilirsiniz:

 
var funcs = [];
for (var i = 0; i < 3; i++) {     
  (funcs[i] = function() {         
    console.log("My value: " + i); 
  })(i);
}
    
6
2017-12-27 00: 06: 45Z

Sahte bir dizi oluşturma ile kendi kendine kapanma özelliği olan forEach işlevini 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
}

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

    
3
2015-12-17 15: 14: 47Z
  1. Neyi tercih edersiniz? Bu, diğer bazı cevaplara cevaben bir yorum gibi görünüyor. Asıl soruya hiç cevap vermiyor (çünkü daha sonra herhangi bir yerde çağrılacak bir fonksiyon atamadığınız için).
    2015-12-17 14: 24: 19Z
  2. Şimdi açık mı?
    2015-12-17 14: 28: 45Z
  3. Tam olarak belirtilen sorunla ilgili: kapatma problemleri olmadan nasıl güvenle yinelenebilir
    2015-12-17 14: 31: 00Z
  4. Şimdi, kabul edilen yanıttan önemli ölçüde farklı görünmüyor.
    2015-12-17 14: 31: 10Z
  5. Hayır. Kabul edilen cevabında "bazı diziler" kullanılması önerilmektedir, ancak cevabın bir aralığını ele alıyoruz, bu kesinlikle farklı şeyler, ki maalesef js'de iyi bir çözümü yok, bu yüzden cevabım sorunu çözmeye çalışıyor. iyi ve pratik bir şekilde
    2015-12-17 14: 34: 33Z

Kodunuz çalışmıyor, çünkü olduğu gibi:

 
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 şu, fonksiyon çağrıldığında i değişkeninin değeri nedir? İlk döngü i < 3 koşulu ile oluşturulduğundan, koşul yanlış olduğunda derhal durur, yani i = 3 olur.

İşlevlerinizi oluşturduğunuz zaman kodlarının hiçbirinin yürütülmediğini ancak daha sonra için kaydedildiğini anlamanız gerekir. Bu yüzden daha sonra çağrıldıklarında, tercüman onları yürütür ve “i'un şu anki değeri nedir?” Diye sorar.

Öyleyse, amacınız önce işlevsellik olarak i değerini, ardından işlevden sonra funcs değerini kaydetmektir. 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 bir fonksiyonun kendi x değişkeni olacaktır ve bunu x'u her yinelemede i değerine ayarladık.

Bu, bu sorunu çözmenin birden fazla yolundan yalnızca biridir.

3
2017-12-26 22: 53: 29Z
 
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
2018-07-13 08: 02: 09Z

var yerine let (blocked-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]();                      
}
    
3
2019-04-15 10: 44: 17Z

query-js (*) gibi veri listeleri için bildirim modülünü kullanabilirsiniz. . Bu gibi durumlarda şahsen daha az şaşırtıcı olmayan, bildirimsel bir 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 yapabilirsiniz

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

(*) Ben sorgulama-js 'in yazarıyım ve bu yüzden kullanmaya önyargılıyım, bu yüzden sözlerimi yalnızca bildirici yaklaşım için söz konusu kütüphane için öneri olarak alma :)

    
2
2015-10-27 10: 15: 11Z
  1. Aşağı oylamanın açıklamasını çok isterim. Kod eldeki sorunu çözer. Kodu potansiyel olarak nasıl geliştireceğinizi bilmek değerli olabilir
    2015-06-18 18: 21: 18Z
  2. Query.range(0,3) nedir? Bu, bu soru için etiketlerin bir parçası değil. Ayrıca, bir üçüncü taraf kütüphanesi kullanıyorsanız, belgelerin bağlantısını da sağlayabilirsiniz.
    2015-10-27 04: 07: 40Z
  3. @ jherax, bunlar ya da elbette belirgin gelişmeler. Yorumunuz için teşekkürler. Zaten bir bağlantı olduğuna yemin edebilirdim. Bu yazı ile oldukça anlamsız sanırım :). Öncelikle bunu sürdürme fikrim, kendi kütüphanemin kullanımını zorlamaya çalışmak değildi, ancak daha fazla beyan edici fikirdi. Ancak görüşmelerde bağlantının orada olması gerektiğine tamamen katılıyorum.
    2015-10-27 10: 17: 32Z

Pek çok çözüm doğru görünüyor, ancak bunun adı Currying Bu gibi durumlar için fonksiyonel bir programlama tasarım deseni. Tarayıcıya bağlı olarak ciltlemeden 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ı konusuna bakın.

    
2
2017-12-26 21: 21: 57Z
  1. @ TinyGiant Döndürülen işleve sahip olan örnek, performans için optimize edilmiş olarak hala köreliyor. Tüm JavaScript blogcuları gibi ok işlevler bandwagonuna atlamam. Serin ve temiz görünüyorlar ancak önceden tanımlanmış işlevleri kullanmak yerine satır içi yazma işlevlerini destekliyorlar. Bu sıcak yerlerde bariz olmayan bir tuzak olabilir. Diğer bir problem ise sadece sözdizimsel olmayan şeker olmalarıdır, çünkü gereksiz bağlamalar yapıyorlar, böylece sarma kapamaları yaratıyorlar.
    2017-12-27 01: 52: 28Z
  2. Gelecekteki okuyuculara uyarı: Bu cevap yanlış bir şekilde Currying terimini uygular. "Birden fazla argüman alan bir argümanı içeren bir dizi işlevi, argümanların bir parçası olan fonksiyonlara ayırdığınızda meydana gelir." . Bu kod sıralamadan hiçbir şey yapmaz. Burada yaptığın tek şey almakKabul edilen cevaptan e kodu alın, bazı şeyleri hareket ettirin, stili ve ismini biraz değiştirin, ardından kategorik olarak olmayan, körleme olarak adlandırın.
    2017-12-27 02: 36: 38Z

Bu, zaman uyumsuz kodla sıkça karşılaşılan bir sorundur, i değişkeni değişkendir ve işlev çağrısının yapıldığı sırada i kullanan kod yürütülür ve i en son değerine dönüştürülür, bu nedenle tüm işlevler anlamına gelir döngü içinde oluşturulan bir kapatma ve i oluşturulacak 3'e (for döngünün üst sınırı + 1'i.)

Buna geçici bir çözüm, her yineleme için i değerini elinde tutacak ve i kopyasını zorlayacak bir işlev oluşturmaktır (ilkel olduğu için, yardımcı olması durumunda anlık görüntü olarak düşünün).

    
2
2017-12-27 00: 10: 53Z
kaynak yerleştirildi İşte