[JS] 스코프, 클로저
JavaScript & TypeScript

[JS] 스코프, 클로저

반응형

스코프 : 배율로 볼수있는 범위가 다르듯이 8배로만 볼 수 있는 물체가 있으면 2배로는 볼 수 없다. 가까운 거리는 2배가 유리하지만 어쨋든 8배로는 볼 수 있다..

 

클로저 : 변수 x, y 두개가 있다고 하면은, 함수안에 함수를 넣어서 x함수를 잠구고 y값을 원하는대로 컨트롤 가능하며, 함수 한번만 짜면은 컨트롤을 여러개 할 수 있다.

 

쉽게 생각하면 이런 느낌이었습니다. 이 때까지 코드를 짜오면서 좀 당연한 규칙(?)인듯한 느낌이었지만 구체적으로 몰랐기 때문에 이번 기회에 배우게 되었습니다.

 

 

1.  스코프

예제 1)
let username = 'kimcoding';
if (username) {
  let message = `Hello, ${username}!`;
  console.log(message); // ?
}

console.log(message); // ?


예제 2)
let greeting = 'Hello';
function greetSomeone() {
  let firstName = 'Josh';
  return greeting + ' ' + firstName;
}

console.log(greetSomeone()); // ?
console.log(firstName); // ?

 

예제 1) "Hello, kimcoding!"     ReferenceError

4번째 줄에서 message를 출력할 때는, 3번째 줄의 username을 바깥 스코프에서 가져왔으므로 정상적으로 출력됩니다.

그러나, 6번째 줄에서는 message라는 변수 자체가 안쪽 스코프에 선언되어 있으므로, 바깥쪽에서는 접근할 수 없습니다.

 

예제 2) 'Hello Josh'      ReferenceError

greeting 변수는 바깥 스코프에 정의되어 있으므로, 함수 안쪽에서 사용할 수 있습니다. 따라서 greeting 변수와 firstName 변수의 조합의 의해 'Hello Josh' 문자열이 출력됩니다.

반면에, firstName 변수는 안쪽 스코프에 정의되어 있으므로 바깥쪽에서는 접근이 불가능합니다. 따라서 ReferenceError를 냅니다.

 

예제 3)
let name = '김코딩';

function showName() {
  let name = '박해커'; // 지역 변수
  console.log(name); // 두번째 출력
}

console.log(name); // 첫번째 출력
showName();
console.log(name); // 세번째 출력




예제 4)
let name = '김코딩';

function showName() {
  name = '박해커';
  console.log(name); // 두번째 출력
}

console.log(name); // 첫번째 출력
showName();
console.log(name); // 세번째 출력

예제 3과 예제 4의 큰 차이는 showName함수 안에 let 변수를 사용했는지 밖에 선언된 변수를 가져왔는지 차이 입니다.

 

예제 3) 김코딩, 박해커, 김코딩

두번째 출력은 함수 안에서 선언한 name이라는 지역 변수에 접근하고 있습니다. 변수 이름이 전역 변수와 똑같지만, 지역 변수가 전역 변수보다 우선순위가 높으므로, 지역 변수 name이 출력되는 것입니다. 동일한 변수 이름으로 인해 바깥쪽 변수가 안쪽 변수에 의해 가려지는(shadow) 이러한 현상을 쉐도잉(variable shadowing)이라고 부릅니다.

 

예제 4) 김코딩, 박해커, 박해커

세번째 줄에서 let 키워드를 사용한 선언이 존재하지 않습니다.

이는, '박해커'라는 값으로 할당하고 있는 name 변수는 전역에 선언된 name 변수를 그대로 사용하겠다는 의미입니다.

 

결국 스코프를 배우면서 주의 해야할 점.

1. 전역변수를 최소화 (전역 변수를 사용하면 지역 스코프에 영향을 줌으로)

2. let, const를 주로 사용화 (var 키워드는 블록 스코프를 무시하기에 불안정함.)

3. 선언 없는 변수 할당 금지

 

 

2. 클로저

정의 : 함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.

 

쉽게 표현하자면 함수 안에 함수를 대입한 것입니다. 그렇기 때문에 변수 x, y가 한번에 대입 되지 않고 차례로 나누어 대입하여 집니다.

 

 

단순화 하자면 함수 안에 함수를 넣었기 때문에, 변수도 같이 변수안에 변수를 넣는다는 느낌으로 사용해 주면 됩니다.

위의 그림을 설명 하자면 x의 값은 5로 고정이 되어 실행이 한번 되면 계속해서 사용이 가능하다는 점 입니다.

 

정보의 제한(x값의 변경 제한)을 특성을 이용하여 모듈화를 만들 수 있습니다.

 

 

위와 같이 모듈화를 하였으면 counter1 , counter2 각각의 변수를 선언해주었을 때에, valuer를 지정해주지 않았는데도 영향이 가지 않습니다. 이러한 편리한 장점이 있습니다.

 

 

코드 예시를 들어 보도록 하겠습니다.

let multiplyByX = function(x) {
  return function(y) {
    return x * y;
  }
}

let multiplyBy5;
multiplyBy5 = multiplyByX(5);

multiplyBy5(4);

클로져 함수는 "외부함수의 컨텍스트에 접근할 수 있는 내부함수" 입니다.

리턴되는 함수가 x에 접근할 수 있고, 이 x가 위치한 곳은 외부함수의 context, x에 접근 가능한 스코프 안 입니다. 이렇게 x를 포함하여 쓰여있는 코드 자체를 어휘적 환경(lexical environment)라고 부를 수 있습니다.

그렇기 때문에, 클로져의 정의 "함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다."로 부터 우리는 "아, x에 접근할 수 있는 이유는, 리턴되는 함수가 선언된 주변의 어휘적 환경 중 x가 포함되어 있기 때문이구나"라고 이해할 수 있고, 이런 함수를 통칭하여 클로져 함수("외부함수의 컨텍스트에 접근할 수 있는 내부함수")라고 알 수 있습니다.

 

장황하게 설명을 하게되면 복잡하고 어려워지기 때문에

함수 내에서 다른 함수(내부 함수)가 리턴이 되면, 이 함수를 클로져 함수라고 부르고, 외부 함수에 있는 변수에 접근 가능하다. 라고 정리 하시면 되겠습니다.

 

그리고 제가 현재 쉽게 기억하려고 하는 방식은

함수안에 함수를 구현하여 x, y값을 한번에 대입하지 않고 나누어 대입을 하고, x값을 고정시켜 y값을 자유롭게 사용 할 수 있는 것으로 생각 합니다.  또한 이 때 사용하는 변수 또한 함수와 마찬가지로 변수안에 변수를 대입하여 넣습니다. 이때 x값을 할당해 주면 됩니다.

결국 스코프와 클로저를 정리를 하자면

안으로 들어가면 들어갈수록 귀해서 쓰기 어렵고, 밖으로 나와 있으면 있을수록 덜 귀해서 쓰기 쉽다 라고 생각하면 될 것 같습니다.

반응형

'JavaScript & TypeScript' 카테고리의 다른 글

[JS] 복습(과제) 정리  (0) 2021.06.01
[JS] Spread, Rest, Destructing  (2) 2021.05.31
[JS] 객체  (1) 2021.05.27
[JS] 배열  (0) 2021.05.25
[JS] 반복문  (0) 2021.05.13