LINUX.ORG.RU

Неточности в определении замыкания в javascript

 , ,


0

1

Интересует два фрагманте кода, оба выглядят как замыкания, но согласно определению не являются замыканием. Вопрос чисто теоретический, хочу создать видеоролик про замыкание и по этому хочу точно разобраться

Определение из mdn (на английском тоже самое)

Замыкание — это комбинация функции и лексического окружения, в котором эта функция была определена. Другими словами, замыкание даёт вам доступ к Scope (en-US) внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз при создании функции, во время её создания.

Два фрагмента кода

const fn = () => {
  let x = 5;
  if (true) {
    console.log(x); // Вот тут x не существует в текущем (if-овском) скоупе, переменная ищется (lookup?) в родительском (fn-овском) 
  }
}

Вопрос, откуда берется значение переменной x? Хочется сказать и думать что из замыкания, но замыкание работает с функциями, тут же блочный скоуп

Или вот например если у нас есть файл

let x = 6;
export const getX = () => x

в данном случае переменная x не находится в скоупе внешней функции, она находится в скоупе модуля

[UPD] Так по факту и не разобрались, но большинство склоняется к тому что второй пример является замыканием, а значит на mdn была неточность.

★★★

Последнее исправление: abs (всего исправлений: 3)
Ответ на: комментарий от abs

Но у меня уже есть канал на ютубе, это лишь еще одно видео которое я хочу снять…

Тогда я рекомендую провести работу. В первых моих постах даны ссылки. Если кто-то сделает что-то похожее на русском, то будет классным учителем.

B0B
()
Ответ на: комментарий от t184256

Аххах. Спасибо, повеселил, и не помню, когда ты так тонко шутил! Благодарю!

И я пока на гланге ;) Пока не забанят.

B0B
()
Последнее исправление: B0B (всего исправлений: 2)
Ответ на: комментарий от B0B

В общем теперь я понял, что внешний код у него выполняется только один раз, так как x - это уже вызов с конкретным значением всей конструкции. Просто вначале не обратил внимания на приписанное в исходном примере (1) в конце.
Фактически да получается, что внутренняя функция один раз создает замыкание, так как её объявление тут происходит единожды, а последующие запуски просто вызывают.

xmikex ★★★★
()
Ответ на: комментарий от monk

Сделай тогда второе замыкание с этими же a и b, но другим значением x.

Кстати, пожалуйста.

let f = []

for(let i = 0; i < 5; i++) f.push(n => n + i)

f // массив f содержит пять одинаковых функций, каждая из которых замкнута на отдельную область видимости, со своим собственным i

f[0](1) // 1
f[4](1) // 5

Ещё один пример того, то замыкание не обязано быть продуктом работы другой функции.

Но как я уже выше написал, никакого требования о том, что замыкание должно иметь возможность инстанцироваться множество раз не существует.

javascript
()
Ответ на: комментарий от cdshines

Module Scope - это один из множества типов Scope. Функция может быть замкнута на любой из таких скоупов. И на модуль в том числе.

javascript
()
Ответ на: комментарий от javascript

Ну, то есть это Outer Functions Scope, правильно? Нужно выбрать 1 из 3:

Every closure has three scopes:

Local Scope (Own scope)
Outer Functions Scope
Global Scope
cdshines ★★★★★
()
Ответ на: комментарий от cdshines

Это Outer, но не Function scope.

Кстати еще и возможен случай с Outer Code Block scope

Это например вот так

const outer = () => {
  let x = 5;
  if (true) {
    let y = 6;
    return () => console.log(x, y);
  }
}
abs ★★★
() автор топика
Последнее исправление: abs (всего исправлений: 1)
Ответ на: комментарий от abs

какой вообще смысл в замыканиях в языке, что в браузере картинки рендерит и кнопочки обслуживает? 99 процентов скрипта на стороне клиента - это минимальные функции в одну строчку.

alysnix ★★★
()
Ответ на: комментарий от cdshines

Ну, то есть это Outer Functions Scope, правильно?

Нет, не правильно. Это просто Outer Scope. И их на пути от Own Scope до Global Scope может быть бесчисленное множество. Они могут быть блочными, функциональными, модульными.

Нужно выбрать 1 из 3:

Не нужно.

Алсо, статья на мдн откуда ты это тащишь писалась во времена, когда в жс не было ни блочных областей видимости, ни модулей. Были только функции.

И еще, спецификация языка вообще не оперирует такими терминами как замыкание (в том смысле о котором этот тред). Замыкание - это общая концепция языков программирования. И в этом общем виде она звучит вот так

Замыкание (англ. closure) в программировании — функция первого класса, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции в окружающем коде и не являющиеся её параметрами. Говоря другим языком, замыкание — функция, которая ссылается на свободные переменные в своей области видимости.

javascript
()
Ответ на: комментарий от javascript

Алсо, статья на мдн откуда ты это тащишь писалась во времена, когда в жс не было ни блочных областей видимости, ни модулей

Черт, а ты прав. Я то поначалу думал что «Last modified: Jul 20, 2021, by MDN contributors» - а потом оказалось что это они .md в .html поменяли, а не исправляли текст статьи.

abs ★★★
() автор топика
Ответ на: комментарий от alysnix

какой вообще смысл в замыканиях в языке, что в браузере картинки рендерит и кнопочки обслуживает? 99 процентов скрипта на стороне клиента - это минимальные функции в одну строчку.

Бред, вот тебе типичный элементарный код на реакте в котором есть замыкание

const Button = () => {
  const [isActive, setActive] = useState(true);

  return <Button onClick={() => setActive(!isActive)} /> 
}
abs ★★★
() автор топика
Ответ на: комментарий от abs

Бред, вот тебе типичный элементарный код на реакте в котором есть замыкание

что там написано понял лишь частично… а без замыканий кнопичку уже не обслужить? такая уж сложная кнопичка там нарисовалась?

…ну просто в нашей деревне, на плюсах, какие только кнопы ни рисовали и какие только контролы ни делали…а об замыкания не стукнулись ни разу. вот и спрашиваю…

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

что там написано понял лишь частично

Тогда зачем утверждать что-то в области в которой не разбираешься?

"-А тебе жена изменяет"
"-Так я девушка, и одинокая"
"-Понял лишь частично, но разве это мешает твоей жене изменять тебе?"
abs ★★★
() автор топика
Последнее исправление: abs (всего исправлений: 1)
Ответ на: комментарий от abs

Тогда зачем утверждать что-то в области в которой не разбираешься?

в области приматывания хуков к кнопкам разбирается любой ващета.

alysnix ★★★
()
Ответ на: комментарий от javascript

Пример в общем то привёл не наглядный. Из него вообще не видно поступает ли в объявленную функцию просто значение текущее переменной i из цикла или же именно отдельная переменная в замыкании создалась. Как по этому примеру понять, что у тебя создаётся замыкание, а не просто подстановка значения из внешней области видимости, то есть можно понять этот код, как создание набора функций
n=>n+0
n=>n+1
...
n=>n+4
А не как наличие реальной переменной в замыкании у каждой функции.

xmikex ★★★★
()
Ответ на: комментарий от xmikex

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

Замыкание это и есть подстановка значение из внешней области видимости

А не как наличие реальной переменной в замыкании у каждой функции.

переменная одна общая между всеми вызовами функции

abs ★★★
() автор топика
Ответ на: комментарий от javascript

Ещё один пример того, то замыкание не обязано быть продуктом работы другой функции.

Тут согласен. Убедил. Хотя очень удивлён, что for на каждую итерацию создаёт новую переменную. Для меня это очередной WTF JS.

Вот лисп

(defvar *a* nil)
(dotimes (i 5) (push (lambda (n) (+ n i)) *a*))
(funcall (nth 0 *a*) 1) ; => 6
(funcall (nth 4 *a*) 1) ; => 6

Хотя на Scheme результат такой же как в JS, потому что там цикл по построению является рекурсивным вызовом функции. Возможно в JS for тоже раскрывается во что-то вроде

(for = (i) => if (i < 5) { f.push(n => n + i); for(i+1); })(0)
monk ★★★★★
()
Ответ на: комментарий от alysnix

а без замыканий кнопичку уже не обслужить? такая уж сложная кнопичка там нарисовалась?

Даже в GObject на Си сделали замыкания. Потому что к действию кнопочки таки часто необходимо добавить какие-то данные. Альтернатива: создавать столько функций, сколько есть кнопочек (например, на этом форуме их по три на каждое сообщение).

monk ★★★★★
()
Ответ на: комментарий от abs

Замыкание это и есть подстановка значение из внешней области видимости

Не значения, а ссылки на внешнюю переменную. Иначе его менять нельзя было бы в теле замыкания

monk ★★★★★
()
Ответ на: комментарий от xmikex
let f = []

for(let i = 0; i < 5; i++) f.push(n => n + i++)

f[0](1) // 1
f[0](1) // 2
f[4](1) // 5
f[4](1) // 6
f[0](1) // 3
monk ★★★★★
()
Ответ на: комментарий от monk

Потому что к действию кнопочки таки часто необходимо добавить какие-то данные.

передаете кнопе функтор,

или указатель на обьект с интерфейсом вида

class ActionBase {
  virtual void onClick() = 0;
}

кнопа дергает onClick(), все данные внутри реального обьекта.

или просто пару - указатель на функцию, указатель на данные. или указатель на такую пару… да куча способов безо всяких замыканий

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)

Ребята, читайте спецификации.

Чтобы не бегать по форумам, блогам, собирая по крупицам информацию.

Спецификация написана занудно, но исчерпывающе.

anonymous
()
Ответ на: комментарий от alysnix

пару - указатель на функцию, указатель на данные.

это и есть замыкание

thesame ★★★★
()
Ответ на: комментарий от alysnix

передаете кнопе функтор,

или указатель на обьект с интерфейсом вида

Так 99 процентов скрипта на стороне клиента - это минимальные функции в одну строчку. Ради каждой из них городить полдюжины строк бойлерплейта для описания объекта/функтора - перебор.

monk ★★★★★
()

Sorry /о JavaScript/

ИМХО с ним та же история, что и Foxpro.
Функционально Foxpro позволял создать систему типа 1С 7.7 и много лучше.
Но никто и ничего на нем не разработали.
Почему?
Потому что многие умеют лишь БЕГАТЬ ЗА БАНЮ.
С JavaScript ныне загадили всякими поделками те кто умеет лишь

БЕГАТЬ ЗА БАНЮ ...
anonymous
()
Ответ на: комментарий от abs

Между вызовами она одна, но для каждого объявления из цикла создаётся замыкание на свою переменную i, которая не имеет отношения к другим.
В том примере просто не показано, что имеется доступ к переменной в замыкании, а не к текущему значению её во время объявления.

xmikex ★★★★
()
Ответ на: комментарий от xmikex

for(let i = 0; i < 5; i++) f.push(n => n + i)

Да, понял, думал о другом речь.

Как по этому примеру понять, что у тебя создаётся замыкание, а не просто подстановка значения из внешней области видимости, то есть можно понять этот код, как создание набора функций

Понять можно

а) просто прочитав как работает замыкание в JS, я опять вижу какое-то гадание на кофейной гуще, хотя все это хорошо описано
б) посмотрев в chrome dev tools дебагере, там показывает переменные из замкнутого scope

в)

for(let i = 0; i < 5; i++) f.push({get: n => n + i, set: ni => i = ni})

f[0].get(5)
5
f[0].get(5)
5
f[0].set(10)
10
f[0].get(5)
15
abs ★★★
() автор топика
Ответ на: комментарий от alysnix

определении замыкания в javascript
в модула-2 все еще прикольней
ИМХО с ним та же история, что и Foxpro.

обожаю LOR.

Shadow ★★★★★
()
Последнее исправление: Shadow (всего исправлений: 1)
Ответ на: комментарий от anonymous

БЕГАТЬ ЗА БАНЮ

Тут ещё круче - так как JS в большинстве случаев обслуживает кривой костыльный HTML с свойствами и стилями в виде DOM, в большинстве случаев хочется плюнуть, забить и наговнокодить.

Shadow ★★★★★
()
Ответ на: комментарий от alysnix

передаете кнопе функтор, или указатель на обьект с интерфейсом вида

Да-да, помню Java7 без лямбда-выражений

бойлерплейтное

button.addActionListener(new ActionListener() {
    @Override
    public void performAction(ActionEvent event) {
        // ...
    }
});

вместо нормального

button.addActionListener(event -> {
    // ...
});

да куча костылей безо всяких замыканий

починил

korvin_ ★★★★★
()
Ответ на: комментарий от abs

Интересно, а что было бы, если бы во время цикла вызовы данных функций происходили. Они бы обращались тогда к просто переменной i, а не к замыкания на неё?

xmikex ★★★★
()
Ответ на: комментарий от xmikex

Они бы обращались тогда к просто переменной i, а не к замыкания на неё?

Обратиться к i это и есть замыкание. И еще раз, замыкаются не переменные, а область видимости в которое эти переменные определены

abs ★★★
() автор топика
Ответ на: комментарий от abs

Не совсем одно и то же это. При вызове функции внутри цикла ей будет видна сама переменная цикла i, а при вызове функции за пределами цикла переменной i изначальной уже не будет, а будет только замыкание с ней.

xmikex ★★★★
()
Ответ на: комментарий от xmikex

ей будет видна сама переменная цикла i

а при вызове функции за пределами цикла переменной i изначальной уже не будет, а будет только замыкание с ней

Это бред. «видна» переменная будет через замыкание, что в первом что во втором случае, поведение будет аналогичным, «под капотом» будет работать аналогично

abs ★★★
() автор топика
Ответ на: комментарий от xmikex

ну ты проверял или просто так считаешь?

Как это можно проверить, если твоя фраза «ей будет видна сама переменная цикла i» просто ничем не отличается от замыкания, это и есть замыкание.

Это тоже самое что говорить «по парным дням мы видим картинку на мониторе, а по непарным монитор излучает свет который попадает в наши глаза». Что тут проверять?

abs ★★★
() автор топика
Ответ на: комментарий от abs

смотри у тебя будет просто код без цикла в котором

let i = 5;
function x()
{   
   i =7;
   echo i;
}
function y()
{
   echo i;
}
x();
y();

У тебя тут будет замыкание?

xmikex ★★★★
()
Последнее исправление: xmikex (всего исправлений: 1)
Ответ на: комментарий от xmikex

да x, y обе функции с замыканием

abs ★★★
() автор топика
Ответ на: комментарий от abs

ты не отличаешь ситуации когда во внешнем scope реальном есть переменная от ситуации, в которой переменная доступна только через замыкание?
когда у двух функций есть доступ к одной переменной - это уже не замыкание, а просто доступная из общего scope переменная.

xmikex ★★★★
()
Последнее исправление: xmikex (всего исправлений: 1)
Ответ на: комментарий от xmikex

ты не отличаешь ситуации когда во внешнем scope реальном есть переменная от ситуации, в которой переменная доступна только через замыкание?

Бывает не реальный скоуп?

Переменная из внешнего скоупа ВСЕГДА доступна ТОЛЬКО через замыкания.

abs ★★★
() автор топика
Ответ на: комментарий от abs

Да бывает реальный scope, называется лексическая среда. В ней имеются доступные в текущий момент переменные, вне зависимости от того какую ты функцию вызываешь.
Замыкание же доступно только той функции, которая была создана в момент доступности переменных.

xmikex ★★★★
()
Последнее исправление: xmikex (всего исправлений: 1)
Ответ на: комментарий от xmikex

Замыкание же доступно только той функции

Не корректно, замыкание не принадлежит функции.

Замыкание предоставляет функции доступ к реальный scope, называется лексическая среда.

abs ★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.