클로저

lee jae hwan·2022년 7월 24일

javascript

목록 보기
34/107
post-thumbnail

자바스크립트 함수는 자신이 생성된 외부 코드블럭에 선언된 변수를 참조할 수 있다. 이러한 성질은 특별한 문법을 사용해야 발생되는것이 아니고 자바스크립트가 내부적으로 렉시컬환경객체들의 체인을 형성함으로써 지원하는 성질로서 함수를 반환하고 반환된 함수가 외부함수들의 지역변수들을 참조할 수 있다는 것을 알고 사용할때 진가를 발휘한다.

counter함수가 호출되면 규칙에따라 렉시컬환경객체가 만들어지고 콜스택에 추가된다. 이때 [[Environment]] 속성에 있는 외부환경객체와 연결된다.

count변수에 접근을 하기위해 먼저 자신의 렉시컬환경객체에서 count변수를 찾는다.

count변수가 없기때문에 연결된 외부환경객체에서 count변수를 찾는다. makeCount 렉시컬환경객체에서 count변수를 찾아 값을 반환하고 값을 증가시킨다.

counter함수는 더이상 수행할 코드가 없으므로 종료된다.




주의사항

couter변수는 반환함수를 참조하고 있으므로 반환함수는 가비지콜렉션대상이 되지 않는다. 반면 makeCount함수는 함수를 반환하는 역활을 했으므로 함수종료후 가비지콜렉션대상이 되어야 한다.

makeCount함수가 소멸되면 counter함수가 다시 호출될때 count변수를 참조할 수 없게 될것이다.

그러나 counter함수는 호출될때마다 count변수를 참조할 수 있다. 이것은 counter함수의 [[Environment]]숨김속성이 makeCount함수를 참조하고 있기때문에 makeCount함수는 counter함수가 소멸되기전까지는 가비지콜렉션대상이 되지 않는다.




let arr = [f(), f(), f()];

f함수가 함수를 반환할때 위와같이 3번 호출되면 3개의 실행컨텍스트가 만들어지고 배열원소로 참조되면 f함수는 3개의 배열원소가 참조하고 가비지콜렉션대상이 되지않는다. 불필요한 실행컨텍스트로 메모리낭비가 되지 않도록 해야 한다.



let x = 1;
function func() {
  console.log(x); // ?
  let x = 2;
}
func();

함수는 호출되었을때 실행컨텍스트가 만들어지고 스택에 추가된다.
func함수의 LexicalEnvironment에는 x변수가 등록되고 초기에는 uninitialized된 상태로 있으며 let지시자를 만났을때 값이 초기화되어 그때부터 접근이 가능하다.
위 코드는 x변수가 초기화되기전에 접근되어 에러가 발생하고 스크립트는 중단된다.



let arr = [1, 2, 3, 4, 5, 6, 7];
function inBetween(num1,num2){
   return function(arg){
      if(num1<=arg && num2>=arg) return true;
      return false;
   }
}
console.log( arr.filter(inBetween(3,6)));

배열 filter메소드이 요구하는 콜백함수의 조건이 무엇인가?
1개 인자를 받아 임의조건에 맞을때 true를 반환하는 함수여야 한다.

inBetween(3,6)함수를 호출해서 filter조건에 맞는 함수를 반환하면 되는 것이다.
inBetween이 반환하는 함수는 inBetween함수의 지역변수들을 참조할 수 있다.

해결순서
arr.filter에 콜백을 제공해야한다.
콜백의 인자는 filter가 제공한다.
범위조건을 인자로 사용하는 함수를 만들어 콜백인자와 매개변수로 범위조건을 만들어 true를 반환하는 함수를 반환하면 된다.



let users = [
   { name: "John", age: 20, surname: "Johnson" },
   { name: "Pete", age: 18, surname: "Peterson" },
   { name: "Ann", age: 19, surname: "Hathaway" },
];

function byField(fieldName){
   return function(a,b){
      return a[fieldName]>b[fieldName]?1:-1;
   }
}
users.sort(byField("name"));
users.sort(byField('age'));
console.log(users);

sort함수에 사용될 함수가 필요하다. byField("name")은 'name'필드이름을 제공할역활을 하는것으로 빨리 인식해야 한다.



function makeArmy() {
   let shooters = [];

   let i = 0;
   while (i < 10) {      
      function shooter() {         
         console.log(i); 
      };
      shooters.push(shooter);
      i++;
   }
   return shooters;
}
let army = makeArmy();
army[0]();
army[9]();

while반복시마다 shooter함수가 만들어져서 배열에 추가되고 배열이 반환되어 배열원소가 호출될때 인덱스값을 출력한다.
shooter함수의 변수i는 외부실행컨텍스에 존재하는 변수이므로 함수가 생성될때의 변수i값하고는 당연히 다르게 되고 최종 변경된 값을 출력하게 된다.
그렇다면 함수생성시 변수i값을 출력하려면 어떻게 해야하나?
자신의 실행컨텍스트에 고유값을 가져야 한다.
i는 외부에서 변하는 변수이므로 shooter함수내부로는 가져올수가 없고 외부변수를 참조할 수 밖에 없다.

while코드블럭의 반복시마다 생성되는 지역변수를 만들고 shooter함수에서 그 변수를 참조하면 지역변수이지만 참조되어 메모리해제되지 않으므로 shooter함수는 그값을 참조할 수 있게 된다.

   while (i < 10) {      
      let val = i;
      function shooter() {         
         console.log(val); 
      };
      shooters.push(shooter);
      i++;
   }

0개의 댓글