Как работят блокерите на 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 на 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() във функция, се използва close. Текстът, който можете да 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 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 просто x се позове на същия обект като age променлива! След няколко обаждания до bar age.memb ще има 2! Тази връзка служи като основа за изтичане на памет с HTML обекти.

3825
21 сент. отговор на Али на 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 юни 2009 г. в 21:49 2011-06-24 21:49

Приемайки този въпрос сериозно, трябва да разберем, че типичен 6-годишен човек е когнитивно способен, въпреки че разбира се, тези, които се интересуват от JavaScript, не са толкова типични.

За развитието на детството: от 5 до 7 години се казва:

Вашето дете ще може да следва две стъпки. Например, ако кажете на детето си: "Отидете в кухнята и вземете торба за боклук", те могат да си спомнят тази посока.

Можем да използваме този пример, за да обясним затварянето, както следва:

Кухнята е близо, в която има местна променлива, наречена trashBags . Вътре в кухнята има функция getTrashBag която получава една торба за боклук и я връща.

Можем да кодираме това в javascript, както следва:

696
02 сент. отговорът е даден dlaliberte 02 sep . 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>