„JavaScript“ vidinių linijų uždarymas yra paprastas, praktiškas pavyzdys.

 var buttons = document.getElementsByTagName("button"); for (var i = 0; i < buttons.length; i++) { // let create 3 functions buttons[i].addEventListener("click", function() { // as event listeners console.log("My value: " + i); // each should log its value. }); } 
 // Some async wait function const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms)); for(var i = 0; i < 3; i++){ wait(i * 100).then(() => console.log(i)); // Log 'i' as soon as each promise resolves. } 

Koks yra šios pagrindinės problemos sprendimas?

2493
15 апр. nickf nustatė balandžio 15 d 2009-04-15 09:06 '09, 09:06 2009-04-15 09:06
@ 42 atsakymai
  • 1
  • 2

Problema ta, kad kintamasis i kiekvienoje iš jūsų anoniminių funkcijų yra susijęs su tuo pačiu kintamuoju už funkcijos ribų.

Klasikinis sprendimas: >

Ką norite padaryti, yra susieti kintamąjį kiekvienoje funkcijoje su atskira pastovia verte už funkcijos ribų:

išteklių , bet aš rekomenduotume, kad 2-jų pakopų vertinimas būtų puikus informacijos šaltinis. 

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

Vis dėlto saugokitės, kad IE9-IE11 ir Edge Edge 14 let bet yra neteisingi aukščiau (jie nesukuria naujo „ i kiekvieną kartą, todėl visos aukščiau išvardintos funkcijos bus užregistruotos 3, lyg mes naudojome var ). Galiausiai „Edge 14“ teisinga.

1909 m
15 апр. atsakymą pateikė harto balandžio 15 d. 2009-04-15 09:18 '09 ne 9:18 2009-04-15 09:18

Pabandykite:

 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](); } 
border=0

Pokytis (2014):

Aš asmeniškai manau, kad @Aust yra naujesnis atsakymas apie .bind yra geriausias būdas tai padaryti dabar. Taip pat yra „lo-dash“ / „pabraukimas“, kai jums nereikia arba norite sugadinti šio „ thisArg .

353
15 апр. Atsakymą pateikė Bjornas Tiplingas 15 d. 2009-04-15 09:10 '09, 9:10, 2009-04-15 09:10

Kitas būdas, kuris nebuvo paminėtas, yra „ Function.prototype.bind

324
11 окт. atsakymas duotas Aust Oct 11 2013-10-11 19:41 '13, 19:41, 2013-10-11 19:41

Išraiškos naudojimas Tiesiogiai vadinama funkcija išraiška , lengviausias ir patogiausias būdas pridėti indekso kintamąjį:

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

Tai siunčia iteratoriui i anoniminę funkciją, kurią apibrėžiame kaip index . Tai sukuria uždarymą, kuriame kintamasis i saugomas vėlesniam naudojimui bet kokiose asinchroninėse funkcijose IIFE viduje.

247
11 окт. atsakymas, kurį pateikė neurosnap 11 okt. 2013-10-11 21:23 '13, 21:23, 2013-10-11 21:23

Bitui vėlavo, bet šiandien šią problemą ištyriau ir pastebėjau, kad daugelis atsakymų nėra visiškai susiję su tuo, kaip „Javascript“ tvarko sritis, kurios iš esmės susilpnėja.

Kaip ir daugelis kitų, problema yra ta, kad vidaus funkcija reiškia tą patį kintamąjį i . Taigi, kodėl mes ne tik sukuriame naują vietinį kintamąjį kiekviename iteracijoje ir vietoj to turime nuorodą į vidinę funkciją?

MDN : 

Svarbu: „JavaScript“ neturi blokų apimties. Su bloku įvesti kintamieji yra susieti su esančia funkcija ar scenarijumi, o jų nustatymų efektai išsaugomi už paties bloko. Kitaip tariant, blokų operatoriai neįeina į regioną. Nors „atskiri“ blokai yra tinkama sintaksė, „JavaScript“ nenorite naudoti atskirų blokų, nes jie nedaro to, ką manote, kad jie daro, jei manote, kad jie daro kažką panašaus į šiuos „C“ blokus arba „Java“

Pakartojama, norint pabrėžti:

„JavaScript“ neturi blokų apimties. Su bloku įvesti kintamieji yra susieti su tam tikra funkcija ar scenarijumi.

Tai matome patikrindami ilocal prieš paskelbdami ją kiekviename iteracijoje:

139
10 апр. Atsakymas pateikiamas woojoo666 10 balandžio. 2015-04-10 12:57 '15, 12:57 2015-04-10 12:57

Kadangi ES6 dabar yra plačiai remiamas, geriausias atsakymas į šį klausimą pasikeitė. ES6 suteikia šiam tikslui aplinką „ let and const . Vietoj to, kad galėtume susižeisti su uždarymu, mes galime paprasčiausiai naudoti „ let kad sukonfigūruoti kilpos regiono kintamąjį tokiu būdu:

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

val bus nukreipti į objektą, būdingą konkrečiam ciklui sukti, ir grąžina teisingą vertę be papildomo uždarymo įrašo. Tai, žinoma, labai supaprastina šią problemą.

const panašus į leidimą su papildomu suvaržymu, kad po pirminio priskyrimo kintamojo pavadinimas negali būti atstatomas į naują nuorodą.

Dabar naršyklės palaikymas skirtas tiems, kurie orientuojasi į naujausias naršykles. const / let šiuo metu yra palaikoma naujausiose „Firefox“, „Safari“, „Edge“ ir „Chrome“ versijose. Jis taip pat palaikomas mazge, ir jūs galite jį naudoti bet kur, naudodami tokius statybinius įrankius kaip Babelis. Čia galite pamatyti darbo pavyzdį: http://jsfiddle.net/ben336/rbU4t/2/

Dokumentai čia:

Tačiau saugokitės, kad IE9-IE11 palaikymas ir „Edge to Edge 14“ let bet gauti pirmiau (jie nesukuria naujo „ i kiekvieną kartą, taigi visos pirmiau minėtos funkcijos parašys 3, tarsi naudojant var ). Galiausiai Edge 14 jį išsprendžia.

129
21 мая '13 в 6:04 2013-05-21 06:04 atsakymą pateikė Ben McCormick , gegužės 13 d. 13 val. 2013-05-21 06:04

Kitas būdas pasakyti, kad jūsų funkcija yra susieta funkcijos vykdymo metu, o ne tada, kai funkcija sukuriama.

Kai sukuriate uždarymą, i yra nuoroda į kintamąjį, apibrėžtą išoriniame regione, o ne jo kopiją, kaip buvo kuriant uždarymą. Jis bus vertinamas vykdymo metu.

Dauguma kitų atsakymų suteikia būdų, kaip dirbti, sukuriant kitą kintamąjį, kuris nekeičia jūsų vertės.

Tik maniau, kad pridėsiu aiškumo paaiškinimą. Dėl asmeninio sprendimo, aš norėčiau eiti su Harto, nes tai yra pats geriausias būdas tai padaryti iš atsakymų. Bet kuris iš paskelbtų kodų veiks, bet norėčiau pasirinkti uždaryti gamyklą, nes reikia parašyti komentarų, kad paaiškintumėte, kodėl skelbiu naują kintamąjį (Freddy ir 1800) arba turiu keistą įmontuotą uždarymo sintaksę („Apphacker“).

80
15 апр. Darren Clark atsakymas, pateiktas balandžio 15 d 2009-04-15 09:48 '09 9:48 val. 2009-04-15 09:48

Ką reikia suprasti, yra kintamųjų, esančių javascript, dydis pagal funkciją. Tai yra svarbus skirtumas, nei pasakyti „C #“, kur jūs turite blokų sritį, o tik kintamojo kopijavimas į vieną iš jų veiks.

Apvyniojimas į funkciją, kuri įvertina grąžinimo funkciją, kaip „aplikatoriaus“ atsakymas, padarys triuką, nes kintamasis dabar turi apimtį.

Taip pat yra raktinis žodis vietoj var, kuris leidžia naudoti blokų taikymo taisyklę. Šiuo atveju kintamasis bus apibrėžtas viduje. Tačiau raktinis žodis nėra praktiškas sprendimas dėl suderinamumo.

 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](); } 
63
15 апр. atsakymą pateikė eglasius 15 balandis 2009-04-15 09:25 '09 9:25 val. 2009-04-15 09:25

Čia yra dar vienas metodo variantas, panašus į „Bjorn“ („Apphacker“), kuris leidžia priskirti reikšmę kintamajam funkcijoje, o ne perduoti jį kaip parametrą, kuris kartais gali būti aiškesnis:

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

Atkreipkite dėmesį, kad bet kuriam jūsų naudojamam metodui index kintamasis tampa tam tikru statiniu kintamuoju, susijusiu su grąžinta vidinės funkcijos kopija. Tai reiškia, kad jo vertės pokyčiai išsaugomi tarp pokalbių. Tai gali būti labai patogu.

51
07 авг. Boann atsakė 07 rugpjūčio mėn 2012-08-07 11:45 '12 11:45 am 2012-08-07 11:45

Tai apibūdina bendrą klaidą naudojant „JavaScript“ uždarymus.

Ši funkcija apibrėžia naują aplinką.

Apsvarstykite:

 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 

Kiekvieną kartą, makeCounter skambinate makeCounter , {counter: 0} sukelia naują objektą. Be to, nauja objekto kopija sukurta taip, kad būtų nuoroda į naują objektą. Taigi, counter1 ir counter2 yra nepriklausomi vienas nuo kito.

Uždarymas cikluose

Naudojant kilpą kilpoje yra sunku.

Apsvarstykite:

 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 

Atkreipkite dėmesį, kad counters[0] ir counters[1] nėra nepriklausomi. Tiesą sakant, jie dirba tuo pačiu tikslu!

Taip yra dėl to, kad yra tik viena obj kopija, suskirstyta į visas kilpos iteracijas, galbūt dėl ​​veiklos priežasčių. Net jei {counter: 0} sukuria naują objektą kiekviename iteracijoje, ta pati objekto kopija tiesiog atnaujinama naudojant nuorodą į naujausią objektą.

Sprendimas yra naudoti kitą pagalbinę funkciją:

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

Tai veikia, nes vietiniai kintamieji funkcijų zonoje yra tiesiogiai paskirstomi, o taip pat kintami argumentai funkcijai, nauji atvejai paskirstomi įvedant.

Išsamią diskusiją rasite klaidose ir naudodami javascript uždaryti

47
20 апр. Atsakė Mave balandžio 20 d 2013-04-20 12:59 '13, 12:59, 2013-04-20 12:59

Paprasčiausias sprendimas:

Vietoj to:

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

kuris įspėja „2“, 3 kartus. Taip yra dėl to, kad anoniminėms funkcijoms, sukurtoms „for loop“, yra tas pats uždarymas, o uždarymo vertė i pati. Naudokite tai, kad išvengtumėte bendro uždarymo:

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

Šios idėjos idėja yra apjungti visą „for loop“ kūną, naudojant IIFE (išraiška su momentinio skambučio funkcija) ir perduoti new_i kaip parametrą ir pataisyti ją kaip i . Kadangi anoniminė funkcija vykdoma nedelsiant, kiekvienos funkcijos, apibrėžtos anoniminėje funkcijoje, vertė i skiriasi.

Atrodo, kad šis sprendimas tinka bet kokiai tokiai problemai, nes tam reikės minimalių pradinio kodo pakeitimų, kurie patiria šią problemą. Tiesą sakant, tai yra dizainas, jis neturėtų būti problema!

43
25 июня '13 в 17:21 2013-06-25 17:21 atsakymą pateikė Kemal Dağ birželio 13 d., 17:21, 2013-06-25 17:21

pabandykite tai trumpiau

  • be masyvo

  • jokio papildomo ciklo


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

http://jsfiddle.net/7P6EN/

26
19 сент. Yilmazburk atsakymas 19 sep . 2013-09-19 17:20 '13, 17:20, 2013-09-19 17:20

Čia pateikiamas paprastas sprendimas naudojant „ forEach (dirba su IE9):

 var funcs = []; [0,1,2].forEach(function(i) { // let 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 run each one to see } 

Spausdinti:

 My value: 0 My value: 1 My value: 2 
22
03 мая '14 в 6:42 2014-05-03 06:42 atsakymą Daryl pateikė gegužės 03 d. 14 d. 6:42 2014-05-03 06:42

Pagrindinė problema, susijusi su OP rodomu kodu, yra ta, kad i niekada neskaito iki antrosios linijos. Norėdami parodyti, įsivaizduokite, kad kode matote klaidą

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

Klaida iš tikrųjų neįvyksta, kol funcs[someIndex] () . Naudojant tą pačią logiką, turėtų būti akivaizdu, kad i vertė i pat nebus surenkama iki šio taško. Kai tik pradinis ciklas baigiasi, i++ prideda i prie vertės 3 , kuri sąlygoja i < 3 sutrikimą ir nutraukia kilpą. Šiame etape i yra 3 , todėl, kai naudojami funcs[someIndex]() , ir i yra vertinama, tai yra 3 - kiekvieną kartą.

Norėdami praeiti, turite įvertinti, kaip tai įvyksta. Atkreipkite dėmesį, kad tai jau įvyko funcs[i] (kai yra 3 unikalūs indeksai). Šią vertę galima užfiksuoti keliais būdais. Vienas iš jų yra perduoti jį kaip parametrą funkcijai, kuri jau buvo parodyta čia kelis kartus.

Kita galimybė - sukurti funkcijų objektą, kuris gali uždaryti šį kintamąjį. Tai galima padaryti tokiu būdu.

jsFiddle Demo

 funcs[i] = new function() { var closedVariable = i; return function(){ console.log("My value: " + closedVariable); }; }; 
22
06 марта '14 в 2:03 2014-03-06 02:03 Travio J atsakymas kovo 6 d. 14 d. 2:03 2014-03-06 02:03

„JavaScript“ veikia „uždaryti“ sritį, prie kurios jie turi prieigą prie deklaracijos, ir išsaugo prieigą prie šios srities, net jei kintamieji šioje srityje pasikeičia.

var _tmr = window._tmr || (window._tmr = []);_tmr.push({id: "2334768", type: "pageView", start: (new Date()).getTime()});(function (d, w, id) {  if (d.getElementById(id)) return;  var ts = d.createElement("script"); ts.type = "text/javascript"; ts.async = true; ts.id = id;  ts.src = (d.location.protocol == "https:" ? "https:" : "http:") + "//top-fwz1.mail.ru/js/code.js";  var f = function () {var s = d.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ts, s);};  if (w.opera == "[object Opera]") { d.addEventListener("DOMContentLoaded", f, false); } else { f(); }})(document, window, "topmailru-code");