Затварања у ЈаваСцрипт-у објашњена примерима

Шта су затварања?

Затварање је комбинација функције и лексичког окружења (опсега) у оквиру које је та функција декларисана. Затварања су основно и моћно својство Јавасцрипта. Овај чланак говори о томе како и зашто о затварању:

Пример

//we have an outer function named walk and an inner function named fly function walk (){ var dist = '1780 feet'; function fly(){ console.log('At '+dist); } return fly; } var flyFunc = walk(); //calling walk returns the fly function which is being assigned to flyFunc //you would expect that once the walk function above is run //you would think that JavaScript has gotten rid of the 'dist' var flyFunc(); //Logs out 'At 1780 feet' //but you still can use the function as above //this is the power of closures

Други пример

function by(propName) { return function(a, b) { return a[propName] - b[propName]; } } const person1 = {name: 'joe', height: 72}; const person2 = {name: 'rob', height: 70}; const person3 = {name: 'nicholas', height: 66}; const arr_ = [person1, person2, person3]; const arr_sorted = arr_.sort(by('height')); // [ { name: 'nicholas', height: 66 }, { name: 'rob', height: 70 },{ name: 'joe', height: 72 } ]

Затварање „памти“ средину у којој је створено. Ово окружење се састоји од локалних променљивих које су биле обухваћене у време када је створено затварање.

function outside(num) { var rememberedVar = num; // In this example, rememberedVar is the lexical environment that the closure 'remembers' return function inside() { // This is the function which the closure 'remembers' console.log(rememberedVar) } } var remember1 = outside(7); // remember1 is now a closure which contains rememberedVar = 7 in its lexical environment, and //the function 'inside' var remember2 = outside(9); // remember2 is now a closure which contains rememberedVar = 9 in its lexical environment, and //the function 'inside' remember1(); // This now executes the function 'inside' which console.logs(rememberedVar) => 7 remember2(); // This now executes the function 'inside' which console.logs(rememberedVar) => 9 

Затварања су корисна јер вам омогућавају да „памтите“ податке, а затим вам омогућавају да радите на тим подацима путем враћених функција. Ово омогућава јавасцрипту да опонаша приватне методе које се налазе у другим програмским језицима. Приватне методе су корисне за ограничавање приступа коду, као и за управљање вашим глобалним простором имена.

Приватне променљиве и методе

Затварачи се такође могу користити за инкапсулирање приватних података / метода. Погледајте овај пример:

const bankAccount = (initialBalance) => { const balance = initialBalance; return { getBalance: function() { return balance; }, deposit: function(amount) { balance += amount; return balance; }, }; }; const account = bankAccount(100); account.getBalance(); // 100 account.deposit(10); // 110

У овом примеру нећемо моћи да приступимо balanceнигде изван bankAccountфункције, што значи да смо управо креирали приватну променљиву. Где је затварање? Па, размислите о томе шта bankAccount()се враћа. Заправо враћа Објект са гомилом функција унутар њега, а опет, када га позовемо account.getBalance(), функција је у стању да „запамти“ своју почетну референцу balance. То је снага затварања, где функција „памти“ свој лексички опсег (временски опсег компајлирања), чак и када се функција извршава изван тог лексичког опсега.

Емулирање променљивих опсега блока.

Јавасцрипт није имао концепт променљивих обима блока. То значи да када је на пример дефинисана променљива унутар форлооп-а, ова променљива је видљива и изван форлооп-а. Па како нам затварања могу помоћи да решимо овај проблем? Хајде да погледамо.

 var funcs = []; for(var i = 0; i < 3; i++){ funcs[i] = function(){ console.log('My value is ' + i); //creating three different functions with different param values. } } for(var j = 0; j < 3; j++){ funcs[j](); // My value is 3 // My value is 3 // My value is 3 }

С обзиром да променљива и нема опсег блока, вредност у све три функције је ажурирана бројачем петљи и креирала је злонамерне вредности. Затварање нам може помоћи да решимо овај проблем стварањем снимка окружења у којем је била функција када је креирана, чувајући њено стање.

 var funcs = []; var createFunction = function(val){ return function() {console.log("My value: " + val);}; } for (var i = 0; i < 3; i++) { funcs[i] = createFunction(i); } for (var j = 0; j < 3; j++) { funcs[j](); // My value is 0 // My value is 1 // My value is 2 }

Касније верзије јавасцрипта ес6 + имају нову кључну реч звану лет која се може користити за давање променљивој блоковског опсега. Такође постоје многе функције (форЕацх) и читаве библиотеке (лодасх.јс) које су посвећене решавању проблема као што су горе објашњени. Они сигурно могу повећати вашу продуктивност, али и даље је изузетно важно имати знање о свим овим проблемима када покушавате да направите нешто велико.

Затварачи имају много посебних апликација корисних при креирању великих јавасцрипт програма.

  1. Емулација приватних променљивих или енкапсулација
  2. Упућивање асинхроних позива на страни сервера
  3. Креирање променљиве опсега блока.

Емулирање приватних променљивих.

За разлику од многих других језика, Јавасцрипт нема механизам који вам омогућава да креирате енкапсулиране променљиве инстанце унутар објекта. Поседовање променљивих јавних инстанци може проузроковати много проблема приликом изградње средњих до великих програма. Међутим, са затварањем, овај проблем се може ублажити.

Слично као у претходном примеру, можете изградити функције које враћају објектне литерале методама које имају приступ локалним променљивим објекта без њиховог излагања. Дакле, чинећи их ефективно приватним.

Затварања вам такође могу помоћи да управљате својим глобалним простором имена како бисте избегли колизије са глобално дељеним подацима. Обично се све глобалне променљиве деле између свих скрипти у вашем пројекту, што ће вам дефинитивно створити пуно проблема приликом изградње средњих до великих програма. Због тога аутори библиотека и модула користе затвараче како би сакрили методе и податке целокупног модула. То се назива образац модула, користи израз функције који се одмах позива и који извози само одређене функције у спољни свет, значајно смањујући количину глобалних референци.

Ево кратког узорка скелета модула.

var myModule = (function() = { let privateVariable = 'I am a private variable'; let method1 = function(){ console.log('I am method 1'); }; let method2 = function(){ console.log('I am method 2, ', privateVariable); }; return { method1: method1, method2: method2 } }()); myModule.method1(); // I am method 1 myModule.method2(); // I am method 2, I am a private variable

Затварања су корисна за хватање нових инстанци приватних променљивих садржаних у „запамћеном“ окружењу, а тим променљивим се може приступити само путем враћене функције или метода.

Вектори

Вектор је можда најједноставнија врста колекције у Цлојуре-у. Можете то замислити као низ у Јавасцрипту. Дефинишемо једноставан вектор:

(def a-vector [1 2 3 4 5]) ;; Alternatively, use the vector function: (def another-vector (vector 1 2 3 4 5)) ;; You can use commas to separate items, since Clojure treats them as whitespace. (def comma-vector [1, 2, 3, 4, 5])

Видећете да користи углате заграде, баш као низ у ЈС-у. Пошто се Цлојуре, попут ЈС, динамички куца, вектори могу да садрже елементе било ког типа, укључујући и друге векторе.

(def mixed-type-vector [1 "foo" :bar ["spam" 22] #"^baz$"])

Додавање предмета у вектор

Можете додати ставке у вектор помоћу conj. Такође можете да додате листу помоћу into, али имајте на уму да intoје намењен спајању два вектора, тако да оба њена аргумента морају бити вектори, а коришћење intoје спорије од коришћења conj.

(time (conj [1 2] 3)) ; => "Elapsed time: 0.032206 msecs" ; [1 2 3] (time (into [1] [2 3])) ; => "Elapsed time: 0.078499 msecs" ; [1 2 3]
:ракета:

ИДЕОне ит!

Преузимање предмета из вектора

You can retrieve items from a vector using get. This is equivalent to using bracket notation to access items in an array in many imperative languages. Items in a vector are 0-indexed, counting from the left.

var arr = [1, 2, 3, 4, 5]; arr[0]; // => 1

In Clojure, this would be written like so:

(def a-vector [1 2 3 4 5]) (get a-vector 0) ; => 1

You can also give get a default value, if you give it an index that isn’t in the array.

;; the list doesn't have 2147483647 elements, so it'll return a string instead. (get a-vector 2147483646 "sorry, not found!") ; => "sorry, not found!"

Converting other collections into vectors

Non-vector data structures can be converted into vectors using the vec function. With hashmaps, this produces a 2D vector containing pairs of keys and values.

(vec '(1 2 3 4 5)) ; => [1 2 3 4 5] (vec {:jack "black" :barry "white"}) ; => [[:jack "black"] [:barry "white"]]

When to use a vector?

Вектор би требало да се користи у готово свим случајевима ако вам је потребна колекција, јер имају најкраћа времена насумичног приступа, што олакшава преузимање предмета из вектора. Имајте на уму да су вектори поређани. Ако редослед није важан, можда је боље користити сет. Такође имајте на уму да су вектори дизајнирани за додавање предмета; ако требате додати ставке, можда ћете желети да користите листу.

Више информација о затварању:

  • Научите ЈаваСцрипт затварање за шест минута
  • Основни водич за затварање у ЈаваСцрипт-у
  • Откријте моћ затварања у ВуеЈС-у
  • Затварање ЈаваСцрипт-а објашњено слањем пакета