Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Замыкания в Javascript (https://javascript.ru/forum/misc/81782-zamykaniya-v-javascript.html)

denis_alekss 29.01.2021 00:00

Замыкания в Javascript
 
Вложений: 1
Очень много инфо о замыканих, но немного размыта информация самого смысла его использования.
Главный смысл замыканий сохранить локальную переменную после завершения функции и использовать при запуске 2-ого, 3-его, n-ого раза запуском главной функции в котором встроена внутренняя?

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <script>
var fn = (function() {
   var numberOfCalls = 0;
   return function() {
      return ++ numberOfCalls;
   }
})();
    </script>
</body>
</html>


Интересует, обычно функцию объявляют вначале круглые скобки, а затем фигурные и там список команд в фигурных, а здесь получается круглые и снова круглые? Разве так еще можно объявлять функцию?
Речь про концовку
Цитата:

})();
Переменной fn присвоилась функция, а затем какие-то скобки еще круглые пошли в самом конце функции, для чего?

Вот показал на скрине, функцию объявляют сразу с двумя подряд круглыми скобками что ли или это одновременно и объявление и вызов этой же функции? Разве так можно?

рони 29.01.2021 00:27

Цитата:

Сообщение от denis_alekss
это одновременно и объявление и вызов этой же функции?

да

Белый шум 29.01.2021 14:39

Цитата:

Сообщение от denis_alekss
Разве так еще можно объявлять функцию?

Это называется IIFE (Immediately Invoked Function Expression) и не имеет отношения к замыканиям.

Цитата:

Сообщение от denis_alekss
и использовать при запуске 2-ого, 3-его, n-ого раза запуском главной функции в котором встроена внутренняя?

Сначала один раз вызывают главную (внешнюю) функцию. Результатом выполнения этой функции должна быть ссылка на внутреннюю функцию, которую уже и вызывают много раз (через полученную ссылку на неё).

denis_alekss 29.01.2021 21:30

Цитата:

Сообщение от Белый шум (Сообщение 533231)
Это называется IIFE (Immediately Invoked Function Expression) и не имеет отношения к замыканиям. х

А описанная внутренняя анонимная функция в функции fn разве не замыкание?

Когда я присваиваю переменной fn функцию, а в ней еще внутренняя анонимная функция, переменная fn содержит ссылку на функцию в функции, то есть 2 функции или ссылку только на анонимную внутреннюю одну?

рони 29.01.2021 21:34

Цитата:

Сообщение от denis_alekss
только на анонимную внутреннюю одну?

:yes:

denis_alekss 29.01.2021 21:41

Это все потому что стоит return function(),
А если бы return не стоял для вызова анонимной функции тогда все равно fn содержал бы ссылку на анонимную функцию или он содержит ссылку только на то что возвращает return главной функции fn?

рони 29.01.2021 21:46

denis_alekss,
console.log(fn)

рони 29.01.2021 21:52

denis_alekss,
если интересно уберите строку var numberOfCalls = 0; но сохраните прежний функционал и даже сделайте его более универсальным.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <script>
var fn = (function() {
   var numberOfCalls = 0;
   return function() {
      return ++ numberOfCalls;
   }
})();

console.log(fn());//1
console.log(fn());//2
console.log(fn());//3

var foo = (function() {
   /*var numberOfCalls = 0;*/
   return function() {
      return ++ numberOfCalls;
   }
})();

console.log(foo());//1
console.log(foo());//2
console.log(foo());//3

    </script>
</body>
</html>

denis_alekss 29.01.2021 22:00

Если запустить ваш вариант, вначале выдаст
1 2 3

А затем ошибку:

Цитата:

Uncaught ReferenceError: numberOfCalls is not defined
foo http://test.ru/:23

Если так:

var foo = (function() {
   /*var numberOfCalls = 0;*/
   return function() {
	   numberOfCalls = 0;
      return ++ numberOfCalls;
   }
})();


Выведет 3 раза подряд цифру 1 вместо 1 2 3

рони 29.01.2021 22:14

denis_alekss,
обе функции должны выдавать одно и тоже, плюс foo будет более универсальной.
это не меняйте
return function() {
      return ++ numberOfCalls;
   }

с остальным делайте, что хотите , var numberOfCalls = 0; не использовать

denis_alekss 29.01.2021 22:25

Вложений: 1
Ничего не менял, запустил ваш код, функция foo не определена выдает, вот фото ниже, сделал скриншот

рони 29.01.2021 22:40

Цитата:

Сообщение от denis_alekss
запустил ваш код,

а зачем? вы что-то добавили, изменили? зачем запускать то, что является условием задачи, а не её решением?

denis_alekss 29.01.2021 22:58

Если для теста написать даже вот так:
var foo = (function() {
   /*var numberOfCalls = 0;*/
  
  alert('Привет');
 return  function () {
	   return alert('Пока');
   }
})();


Вначале простреляет 1 Привет, а затем 3 пока, и каждый раз когда будет выводится слово Пока, будет выводится в консоль undefined, значит переменная foo ссылается только на внутренню анонимную функцию ?

НА эту если возвратимся к коду в 1-ом посте:

return function() {
      return ++ numberOfCalls;
   }

рони 29.01.2021 22:59

denis_alekss,
надо дописать в пару мест недостающее, что-бы код стал рабочим.
var foo = (function() {
   return function() {
      return ++ numberOfCalls;
   }
})();

рони 29.01.2021 23:09

denis_alekss,
ок ... что-то пошло не так, наверно я плохо сформулировал задачу...

var foo = (function(numberOfCalls) {
   return function() {
      return ++numberOfCalls;
   }
})(0);

console.log(foo());//1
console.log(foo());//2
console.log(foo());//3

denis_alekss 29.01.2021 23:14

Этим кодом вы показываете что ссылка все-таки идет на внутреннюю функцию, вы просто передаете аргумент 0 функции fn, а им пользуется внутренняя анонимная функция. Если передать вместо 0 передать к примеру 8, счетчик пойдет с 9 из-за инкремента и вся работа пойдет во внутренней а не со внешней функции

рони 29.01.2021 23:27

denis_alekss,
:yes:

denis_alekss 29.01.2021 23:45

Немного другая версия этого же случая:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <script>
Function.prototype.bind = function(context) {
   var fn = this;
   return function() {
      return fn.apply(context, arguments);
   };
}
var HelloPage = {
   name: 'Вася!!!',
   init: function() {
      alert('Hello, ' + this.name);
   }
}
window.onload = HelloPage.init.bind(HelloPage); 
</body>
</html>


Если поменять эту строку:
Function.prototype.bind

на
Function.prototype.call

разницы при выводе не происходит.

Если поменять эту строку:
window.onload = HelloPage.init.bind(HelloPage);

на
window.onload = HelloPage.init.apply(HelloPage);

Также вывод одинаковый.

В этом месте также используется замыкание
Function.prototype.call = function(context) {
   return function() {
      return this.apply(context, arguments);
   };
}

Здесь this ссылается на объект HelloPage благодаря функции call или bind если указать в строке Function.prototype.bind? Можно ли обойтись в этом коде для получения идентичного результата без замыканий если я хочу вызвать метод объекта передав свойство Петя с этого же объекта в метод?

Белый шум 30.01.2021 04:11

Цитата:

Сообщение от denis_alekss
А описанная внутренняя анонимная функция в функции fn разве не замыкание?

Замыкание, но оно не имеет отношения к IIFE.

Можно определить замыкание и без IIFE, просто внешнюю функцию надо будет вызвать явно:
<body>
<script>
function getMyClosure(){
   var count = 0;
   var closure = function(){ return count++; }
   return closure;
}

var counter = getMyClosure(); //вызываем явно и сохраняем ссылку на внутреннюю функцию в нашей переменной
</script>
<button onclick="alert(counter())">Press Me</button>
</body>

denis_alekss 31.01.2021 18:58

Цитата:

var counter = getMyClosure(); //вызываем явно и сохраняем ссылку на внутреннюю функцию в нашей переменной
Зачем нужно создавать ссылку counter на функцию getMyClosure?
Почему нельзя просто вызвать getMyClosure() и передать в Onclick?

Белый шум 01.02.2021 04:27

Цитата:

Сообщение от denis_alekss
Зачем нужно создавать ссылку counter на функцию getMyClosure?

Это не ссылка на функцию getMyClosure. Это выполнение функции getMyClosure и сохранение результата её выполнения в переменную counter. В данном случае функция getMyClosure возвращает ссылку на свою внутреннюю функцию (closure), и в переменную counter попадёт именно она - closure.

Цитата:

Сообщение от denis_alekss
Почему нельзя просто вызвать getMyClosure() и передать в Onclick?

Что передать в onclick? Вызов функции getMyClosure? Так она же возвращает не число, а ссылку на функцию closure... Причём при каждом вызове она будет возвращать новое замыкание, со своим собственным окружением:
<body>
<script>
function getMyClosure(){
   var count = 0;
   var closure = function(){ return count++; }
   return closure;
}
 
var counter1 = getMyClosure(); //первое замыкание
var counter2 = getMyClosure(); //второе
var counter3 = getMyClosure(); //третье
</script>
<button onclick="document.getElementById('btn1').innerText=counter1()">Button1 (<span id="btn1">-</span>)</button>
<button onclick="document.getElementById('btn2').innerText=counter2()">Button2 (<span id="btn2">-</span>)</button>
<button onclick="document.getElementById('btn3').innerText=counter3()">Button3 (<span id="btn3">-</span>)</button>
</body>


P.S.: Если ещё не читали: https://learn.javascript.ru/closure


Часовой пояс GMT +3, время: 06:57.