closure - garevna/js-course GitHub Wiki

:mortar_board: Замыкание

Замыкание - это прием, с помощью которого можно "скрыть" переменные в контексте родительской функции, возвращающей функцию

Замыкание - это комбинация: функция + лексическая среда, в которой эта функция была объявлена

Если функция возвращает функцию, то Lexical Environment родительской функции попадает в цепочку областей видимости дочерней функции

Т.е. все аргументы, полученные родительской функцией при вызове, а так же все переменные и функции, объявленные в ней, будут доступны дочерней функции ( она их "видит" )

Однако больше нигде они доступны не будут, поскольку являются локальными

:coffee: 1

function parent ( arg ) {
  var frog = "I'm frog"
  return function () {
    console.log ( arg, frog )
  }
}

var child = parent ( "Hello! " )

Вызовем дочернюю функцию:

child()  // Hello!  I'm frog

:coffee: 2

var parent = message => () => console.log ( message )

var hello = parent ( "Hello!" )
var welcome = parent ( "Welcome!" )

hello()    // Hello!
welcome()  // Welcome!

Иногда альтернативой замыканию является другой механизм - карринг:

:coffee: 3

var parent = message => console.log ( message )

var hello = parent.bind ( null, "Hello!" )
var welcome = parent.bind ( null, "Welcome!" )

hello()    // Hello!
welcome()  // Welcome!

:coffee: 4

function parent ( omega ) {
    var alpha = 0
    return () => omega > alpha ? 
           omega-- - alpha++ : null
}

var child = parent ( 20 )

Теперь каждый очередной вызов функции child будет возвращать новое число меньше предыдущего на 2 и так до тех пор, пока возвращаемое значение больше 0

child() // 20
child() // 18
...

:mortar_board: IIFE

Immediately Invoked Function Expression

В следующем примере мы видим функциональное выражение IIFE, т.е. вызов объявляемой анонимной функции "на месте" ( в месте ее объявления )

Эта анонимная функция получает агрумент "Hello", объявляет локальную ( приватную ) переменную message, в которой сохраняет полученное значение аргумента, и возвращает анонимную функцию

При этом ее контекст демонтируется ( удаляется из стека ),

но!

переменная message не удаляется из памяти, поскольку возвращаемая анонимная функция использует в своем контексте ссылку на нее

после вычисления значения функционального выражения в переменной func будет ссылка на анонимную функцию, в контексте которой будет "замкнута" переменная message

Теперь ссылка на переменную message существует только в контексте возвращаемой функции

:coffee: 5

var func = ( function ( arg ) {
    var message = arg
    return function ( name ) {
        console.log ( `${ message }, ${ name }` )
    }
}) ( "Hello" )

func ( "Дима" )
func ( "Николай" )

:warning: Function

Функции, созданные конструктором Function, :heavy_exclamation_mark: не создают замыкания

:coffee: 6

var __var__ = "Global Scope"

function functionConstructor() {
    var __var__ = "Function Scope"
    return new Function ( "console.warn ( __var__ )" )
}

function functionDeclaration() {
    var __var__ = "Closured Scope"
    return function () {
        console.info ( __var__ )
    }
}

functionConstructor()()   // Global Scope
functionDeclaration()()   // Closured Scope 

:briefcase: Тесты