closure is simply that a combination of function and the lexical enviroment from which it was declared
클로저는 함수와 그 함수가 선언되었을 때 렉시컬 환경과의 조합이다.
function a() {
let grandpa = 'grandpa'
return function b() {
let father = 'father'
let random = 123
return function c() {
let son = 'son'
return `${grandpa} > ${father} > ${son}`
}
}
}
a()()()
// grandpa > father > son
function c에서 grandpa랑 father에 어떻게 접근할 수 있었을까?
call stack에서 일어나는 일을 한번 따라가보자.
먼저 a 함수가 호출되면 코드를 실행 후 함수 b를 반환하고 call stack에서 제거된다. b 함수도 a 함수와 같은 프로세스를 따른다.
두 함수 모두 call stack에서 제거될 때 실행 컨텍스트가 제거되는 것이므로 실행 컨텍스트의 변수 환경도 사라진다.
함수 호출이 끝나면 가비지 컬렉터에 의해 렉시컬 환경은 메모리에서 제거되어야 하는데 하위 스코프에서 참조하고 있는 변수면 변수면 메모리에서 제거하지 않는다.
c함수를 return 할 때 c의 변수환경에서 먼저 변수들을 찾는다. 변수가 없으면 없으면 grobal scope나 global variable에서 찾는 대신에 스코프 체인으로 필요한 변수를 찾는다.
클로저는 렉시컬 스코핑이라고도 불린다. 렉시컬은 코드가 어디서 선언되었는지를 의미하고 스코핑은 어떤 변수에 접근 가능한지를 의미한다.
스코프체인은 함수 호출 시 생성된다. 함수 컨텍스트의 프로퍼티 중 하나이다. 활성 객체와 이 함수의 내부 프로퍼티인 [[Scope]]로 구성되어 있다.
활성 객체: 함수 컨텍스트의 변수 객체는 활성 객체를 가리킨다. 내부 함수, 지역 변수, arguments 객체를 프로퍼티로 갖는다.
[[Scope]]: 함수 객체의 숨김 프로퍼티. 함수의 생성 단계에서 결정된다. 자신의 실행 환경인 Lexical Enviroment와 자신을 포함하는 외부 실행 환경과 전역 객체를 가리킨다.
function boo(string){
return function(name){
return function(name2){
console.log(`${string} ${name} ${name2}`)
}
}
}
const boo = string => name => name2 => console.log(`${string} ${name} ${name2}`)
const booString = boo('hi')
const booStringName = booString()
function callMeMaybe() {
const callMe = 'Hi! I am now here!'
setTimeout(function() {
console.log(callMe);
}, 4000)
}
callMeMaybe();
function callMeMaybe() {
setTimeout(function() {
console.log(callMe);
}, 4000)
const callMe = 'Hi! I am now here!'
}
callMeMaybe();
function heavyDuty(index){
const bigArray = new Array(7000).fill('hi')
return bigArray[index]
};
heavyDuty(300);
function heavyDuty2(){
const bigArray = new Array(7000).fill('hi')
return function (index){
returnbigArray[index]
}
};
const getHeavyDuty = heavyDuty();
getHeavyDuty(100);
getHeavyDuty(200);
getHeavyDuty(300);
const makeNuclearButton = () => {
let timeWithoutDestruction = 0;
const passTime = () => timeWithoutDestruction++;
const totalPeaceTime = () => timeWithoutDestruction
const launch = () => return {
timeWithoutDestruction = -1;
return 'Boom!'
};
setInterval(passTime, 1000);
return{
launch: launch,
totalPeaceTime: totalPeaceTime
}
}
const ohno = makeNuclearButton();
let view;
function initialize() {
view = 'hi'
console.log('view has been set!')
}
initialize()
initialize()
initialize()
let view;
function initialize() {
let called = 0;
return () => {
if(called > 0) return;
view = 'hi'
called++
console.log('view has been set!')
}
}
cosnt startOnce = initialize();
startOnce();
const array = [1, 2, 3, 4];
for(var i=0; i<array.length; i++){
setTimeout(function(){
console.log('I am at index' + i)
}, 3000)
}
const array = [1, 2, 3, 4];
for(let i=0; i<array.length; i++){
setTimeout(function(){
console.log('I am at index' + i)
}, 3000)
}
let allows us to use block scoping so that this block(for 루프) which is these curly brackets create scope for each i
so i is scoped with here
vs
var i => it was part of gloval scope because we didn't really have fucntion surrounding it so that this set time out when it finally returned by that point, for loop has already gone thorugh everything and has turned i into four
const array = [1, 2, 3, 4];
for(var i=0; i<array.length; i++){
(function() {
setTimeout(function(){
console.log('I am at index' + i)
}, 3000)
})
}