Як працуюць блакавання JavaScript?

Як бы вы патлумачылі закрыццё JavaScript для кагосьці, у каго ёсць веды аб канцэпцыях, з якіх яны складаюцца (напрыклад, функцыі, зменныя і да т.п.), Але не разумеюць саміх замыканняў?

Я бачыў прыклад схемы, прыведзены ў Вікіпедыі, але, на жаль, гэта не дапамагло.

7654
21 сент. зададзены e-satis 21 сент. 2008-09-21 17:12 '08 у 17:12 2008-09-21 17:12
@ 89 адказаў
  • 1
  • 2
  • 3

Закрыццё JavaScript для пачаткоўцаў

Прадстаўлена Morris on Tue, 2006-02-21 10:19. Супольнасць адрэдагавана з тых часоў.

Закрыццё не чараўніцтва

На гэтай старонцы тлумачыцца закрыццё, каб праграміст мог іх зразумець - выкарыстоўваючы працоўны код JavaScript. Гэта не для гуру або функцыянальных праграмістаў.

Закрыцьцё не складана зразумець, як толькі асноўнае паняцце будзе зашыта. Аднак іх немагчыма зразумець, прачытаўшы тэарэтычныя або акадэмічна абгрунтаваныя тлумачэнні!

Гэты артыкул прызначана для праграмістаў з некаторым вопытам праграмавання на асноўнай мове і можа чытаць наступную функцыю JavaScript:

першакласных функцый ;  гэты выраз, якое можа спасылацца на зменныя ў межах сваёй вобласці (калі яно было абвешчана раней), прызначацца зменнай, перадавацца як аргумент функцыі або вяртацца як вынік функцыі. 

прыклад закрыцця

Наступны код вяртае спасылку на функцыю:

 function say667() { // Local variable that ends up within closure var num = 42; var say = function() { console.log(num); } num++; return say; } var sayNumber = say667(); sayNumber(); // logs 43 

Прыклад 4.

Усе тры глабальныя функцыі маюць агульную спасылку на адно і тое ж закрыццё, таму што ўсе яны абвешчаныя на працягу аднаго выкліку setupSomeGlobals() .

 function sayAlice() { var say = function() { console.log(alice); } // Local variable that ends up within closure var alice = 'Hello Alice'; return say; } sayAlice()();// logs "Hello Alice" 

Tricky: таксама звярніце ўвагу, say пераменная say таксама знаходзіцца ўнутры замыкання, і да яе можа быць sayAlice() любая іншая функцыя, якая можа быць аб'яўлена ў sayAlice() , або да яе можна было б атрымаць рэкурсіўна ўнутры ўнутранай функцыі.

Прыклад 6.

Гэта для ўсіх людзей сапраўдная магія, таму вам трэба гэта зразумець. Будзьце вельмі асцярожныя, калі вы вызначаеце функцыю ўнутры цыкла: лакальныя зменныя з замыкання могуць не дзейнічаць так, як вы маглі б спачатку падумаць.

Вам трэба зразумець функцыю "зменнай ўздыму" у Javascript, каб зразумець гэты прыклад.

 function newClosure(someNum, someRef) { // Local variables that end up within closure var num = someNum; var anArray = [1,2,3]; var ref = someRef; return function(x) { num += x; anArray.push(num); console.log('num: ' + num + '; anArray: ' + anArray.toString() + '; ref.someVar: ' + ref.someVar + ';'); } } obj = {someVar: 4}; fn1 = newClosure(4, obj); fn2 = newClosure(5, obj); fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4; fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4; obj.someVar++; fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5; fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5; 

рэзюмэ

Калі ўсё здаецца цалкам незразумелым, лепш за ўсё пагуляць з прыкладамі. Чытанне тлумачэнняў нашмат складаней, чым разуменне прыкладаў. Мае тлумачэнні зачыненняў і стековых фрэймаў і г.д. Не з'яўляюцца тэхнічна правільнымі - гэта грубыя спрашчэння, закліканыя дапамагчы зразумець. Пасля таго, як асноўная ідэя будзе вырашана, вы можаце атрымаць дэталі пазней.

Канчатковыя пункты:

  • Кожны раз, калі вы выкарыстоўваеце function ўнутры іншай функцыі, выкарыстоўваецца закрыццё.
  • Кожны раз, калі вы выкарыстоўваеце eval() ўнутры функцыі, выкарыстоўваецца закрыццё. Тэкст, які вы eval можа спасылацца на лакальныя зменныя функцыі, і ў eval вы можаце нават ствараць новыя лакальныя зменныя з дапамогай eval('var foo = …')
  • Калі вы выкарыстоўваеце new Function(…) ( канструктар функцыі ) усярэдзіне функцыі, яна не стварае закрыццё. (Новая функцыя не можа спасылацца на лакальныя зменныя знешняй функцыі.)
  • Закрыццё ў JavaScript падобна захоўванню копіі ўсіх лакальных зменных, як і пры выхадзе з функцыі.
  • Верагодна, лепш за ўсё падумаць, што замыканне заўсёды ствараецца як запіс функцыі, а лакальныя зменныя дадаюцца да гэтага закрыцця.
  • Новы набор лакальных зменных захоўваецца кожны раз, калі выклікаецца функцыя з замыканнем (улічваючы, што функцыя ўтрымлівае ўнутры яе дэкларацыю функцыі, альбо спасылка на гэтую ўнутраную функцыю альбо вернута, альбо знешняя спасылка захоўваецца для яе нейкім чынам).
  • Дзве функцыі могуць выглядаць так, як быццам яны маюць адзін і той жа зыходны тэкст, але маюць зусім іншае паводзіны з-за іх схаванага закрыцця. Я не думаю, што JavaScript-код сапраўды можа даведацца, ці ёсць у функцыі спасылка закрыццё ці не.
  • Калі вы спрабуеце выканаць любыя змены дынамічнага зыходнага кода (напрыклад: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola')); ), ён не будзе працаваць, калі myFunction з'яўляецца закрыццём (вядома, вы нават не падумалі б пра замену радкоў зыходнага кода падчас выканання, але ...).
  • Можна атрымаць аб'явы функцый ўнутры дэкларацый функцый ўнутры функцый mdash, і вы можаце атрымаць закрыццё на больш чым адным узроўні.
  • Я думаю, што звычайна замыканне з'яўляецца тэрмінам як для функцыі, так і для захопленых зменных. Звярніце ўвагу, што я не выкарыстоўваю гэта вызначэнне ў гэтым артыкуле!
  • Я падазраю, што замыкання ў JavaScript адрозніваюцца ад тых, якія звычайна сустракаюцца на функцыянальных мовах.

сувязі

дзякуючы

Калі вы толькі што даведаліся пра закрыццё (тут ці дзе-небудзь яшчэ!), То мяне цікавіць любая зваротная сувязь ад вас аб любых зменах, якія вы маглі б прапанаваць, каб зрабіць гэтую артыкул больш яснай. Адправіць паведамленне для morrisjohns.com (morris_closure @). Звярніце ўвагу, што я не гуру на JavaScript - ні на закрыццё.


Арыгінальны пост Морыса можна знайсці ў Інтэрнэт-архіве .

6329
21 сент. адказ дадзены Joel Anair 21 сент. 2008-09-21 17:18 '08 у 17:18 2008-09-21 17:18

Кожны раз, калі вы бачыце ключавое слова function ў іншай функцыі, унутраная функцыя мае доступ да пераменным ў знешняй функцыі.

 function foo(x) { var tmp = 3; return function (y) { console.log(x + y + (++tmp)); // will also log 16 } } var bar = foo(2); // bar is now a closure. bar(10); 

Вышэйзгаданая функцыя таксама будзе запісваць 16, таму што bar ўсё яшчэ можа спасылацца на x і tmp , нават калі ён больш не знаходзіцца ўнутры вобласці.

Аднак, паколькі tmp ўсё яшчэ вісіць вакол закрыцця ўнутранай bar , ён таксама павялічваецца. Ён будзе павялічвацца кожны раз, калі вы выклікаеце bar .

Найпростым прыкладам замыкання з'яўляецца наступнае:

border=0

 function foo(x) { var tmp = 3; return function (y) { console.log(x + y + tmp); x.memb = x.memb ? x.memb + 1 : 1; console.log(x.memb); } } var age = new Number(2); var bar = foo(age); // bar is now a closure referencing age. bar(10); 

Як і чакалася, кожны выклік bar(10) будзе павялічваць x.memb . Нельга чакаць, што x проста спасылаецца на той жа аб'ект, што і age пераменная! Пасля пары званкоў у bar age.memb будзе 2! Гэтая спасылка служыць асновай для уцечак памяці з аб'ектамі HTML.

3825
21 сент. адказ дадзены Ali 21 сент. 2008-09-21 18:16 '08 у 18:16 2008-09-21 18:16

Прадмова: гэты адказ быў напісаны, калі пытанне было:

Як і стары Альберт, ён сказаў: "Калі вы не можаце растлумачыць гэта шасцігадовага дзіцяці, вы самі гэтага не разумееце". Добра, я паспрабаваў растлумачыць закрыццё JS 27-гадоваму сябру і цалкам пацярпеў няўдачу.

Ці можа хто-небудзь падумаць, што мне 6, і дзіўна цікавіцца гэтым пытаннем?

Я амаль упэўнены, што я быў адным з тых людзей, якія спрабавалі ўзяць пачатковы пытанне літаральна. З тых часоў гэтае пытанне некалькі разоў мутаваў, таму мой адказ зараз можа здавацца неверагодна дурным і недарэчным. Спадзяюся, агульная ідэя гэтай гісторыі застаецца для некаторых цікавай.


Я вялікі прыхільнік аналогіі і метафары пры тлумачэнні складаных паняццяў, таму дазвольце мне паспрабаваць свае сілы ў гісторыі.

Даўным даўно:

Была прынцэса ...

 function princess() { 

Яна жыла ў выдатным свеце, поўным прыгод. Яна сустрэла свайго прынца Шарля, паехала вакол свайго свету на аднарога, змагалася з драконамі, сустракалася з гаваркімі жывёламі і многімі іншымі фантастычнымі рэчамі.

  var adventures = []; function princeCharming() {  } var unicorn = {  }, dragons = [  ], squirrel = "Hello!";  

Але ёй заўсёды даводзілася вяртацца ў свой сумны свет клопатаў і дарослых.

  return { 

І яна часта расказвала ім пра сваім апошнім дзіўным прыгодзе ў якасці прынцэсы.

  story: function() { return adventures[adventures.length - 1]; } }; } 

Але ўсё, што яны ўбачылі, гэта маленькая дзяўчынка ...

 var littleGirl = princess(); 

... распавядаючы гісторыі пра магіі і фантазіі.

 littleGirl.story(); 

І хоць дарослыя ведалі пра сапраўдныя прынцэсах, яны ніколі не паверылі б у аднарогаў або драконаў, таму што яны ніколі іх не ўбачылі. Дарослыя сказалі, што яны існуюць толькі ўнутры ўяўлення маленькай дзяўчынкі.

Але мы ведаем сапраўдную ісціну; што маленькая дзяўчынка з прынцэсай ўнутры ...

... на самай справе прынцэса з маленькай дзяўчынкай ўнутры.

2273
24 июня '11 в 21:49 2011-06-24 21:49 адказ дадзены Jacob Swartwood 24 чэрвеня '11 у 21:49 2011-06-24 21:49

Прымаючы гэтае пытанне сур'ёзна, мы павінны высветліць, што тыповы 6-гадовы чалавек здольны кагнітыўнай, хоць, па агульным прызнанні, той, хто цікавіцца JavaScript, не так тыповы.

Аб развіцці дзяцінства: ад 5 да 7 гадоў гаворыцца:

Ваш дзіця зможа прытрымлівацца двухэтапную напрамках. Напрыклад, калі вы скажаце свайму дзіцяці: "Ідзіце на кухню і вазьміце мяшок для смецця", яны змогуць запомніць гэты кірунак.

Мы можам выкарыстоўваць гэты прыклад для тлумачэння замыканняў наступным чынам:

Кухня - гэта закрыццё, у якім ёсць лакальная зменная, званая trashBags . Ўнутры кухні ёсць функцыя getTrashBag якая атрымлівае адзін мяшок для смецця і вяртае яго.

Мы можам кадзіраваць гэта ў JavaScript наступным чынам:

696
02 сент. адказ дадзены dlaliberte 02 сент. 2011-09-02 18:23 '11 у 18:23 2011-09-02 18:23

саламяны чалавек

Мне трэба ведаць, колькі разоў націснутая кнопка і нешта рабіць на кожным трэцім кліку ...

Даволі відавочнае рашэнне

 <button id="button">Click Me!</button> 

Цяпер гэта будзе працаваць, але яно ўрываецца ў навакольнае вобласць, дадаючы зменную, адзінай мэтай якой з'яўляецца адсочванне рахунку. У некаторых сітуацыях гэта было б пераважней, так як вашаму вонкавым дадаткам можа спатрэбіцца доступ да гэтай інфармацыі. Но в этом случае мы меняем только каждый третий клик, поэтому рекомендуется включать эту функциональность внутри обработчика событий .

Рассмотрим этот вариант

 <button id="button">Click Me!</button>