코로 넘어져도 헤딩만 하면 그만
주말공부:스코프, 클로저 복습 with Unit9 종합퀴즈 본문
스코프, 클로저 부분에 대해 얼추 이해를 했다고 생각했습니다.
하지만 주말에 복습하면서 다시 보니 헷갈리는 부분들이 있어서 개념부터 다시 찬찬하게 살펴보고 이미 한번 풀었던(몇 개는 틀렸던ㅋㅋ)퀴즈를 풀어보았습니다. 스코프와 클로저뿐만이 아니라 JS내부에서 쓰이는 함수, 매개변수의 흐름 자체에 대한 깊은 학습이 필요해 보였습니다. 무엇보다 더 어려운 개념들 배우다가 돌아오니 당시 따라가는 데에 급급하여 보이지 않던 부분들이 새롭게 보이는 장점이 있었어요.
가령 아래와 같은 문제의 결과가 왜 달라지는지. ...
함수 get 내부에 매개변수로 x가 존재하느냐, 아니냐에 따라서 값이 바뀌죠.
외부의 전역변수 x를 끌어올 것이냐, 아니면 매개변수로 들어오는 x를 사용할 것이냐...
let x = 30;
function get () {
return x;
}
let result = get(20);
let x = 30;
function get (x) {
return x;
}
let result = get(20);
위 결괏값은 30, 아래는 20입니다. 매개변수 x의 유무로 result의 값이 완전히 바뀌는 것을 볼 수 있어요.
let x = 30;
function get () { return x; }
function set (value) { x = value; }
set(10);
let result = get(20);
이런 문제는 어떨까요. 우선 result의 결과는 10으로, 전역 변수인 30을 위에서 set가 10으로 바꿨기 때문입니다. 이 흐름이 꽤나 헷갈립니다. 반대로 아래 문제와 같이 get 함수의 내부에 매개변수 x로 넣으면 결과가 20이 나옵니다.
let x = 30;
function get (x) { return x; }
function set (value) { x = value; }
set(10);
let result = get(20);
그럼... 아래와 같은 문제를 한번 볼까요?
let x = 10;
function outer () {
let x = 20;
function inner () {
x = x + 10;
return x;
}
inner();
}
outer();
let result = x;
함수 out의 내부에 함수 inner이 있습니다. outer은 내부 변수 x가 20이고, inner은 x에 10을 더하는 함수입니다. 함수 inner은 x를 갖고 있지 않으니 상위 스코프에서 20을 x로 가져옵니다. 즉 inner이 실행되면 함수 outer의 변수 x는 30으로 값이 바뀝니다.
outer을 실행한 순간, inner을 거쳐 함수 outer 내부의 변수는 이미 30으로 바뀐 상황입니다. 그러나 마지막 줄에서 result는 전역 스코프의 변수인 x를 불러오고 있습니다. 따라서 답은 10이 됩니다. 함수 outer의 호출이 전역변수에는 영향을 주지 못한 것을 확인할 수 있네요.
이런 문제는 어떨까요?
let x = 10;
function outer () {
x = 20;
function inner () {
let x
x = x + 20;
return x;
}
inner();
}
outer();
let result = x;
이 역시 위와 유사하게 돌아가고 있습니다.
다만 다른 점이 있다면, outer함수의 내부에서 변수 x를 선언한 것이 아니라 전역변수인 x를 20으로 바꿔주고 있군요? (let가 쓰이지 않았음.) 그러니 result에서 호출한 전역 변수 x는 outer의 실행 뒤 20으로 바뀌어 도출됩니다. inner이 outer에서 호출되고 있지만 이 함수는 바깥의 결과에 전혀 영향을 주지 않네요. 정말 어질어질합니다.
let x = 10;
function outer () {
x = 20;
function inner () {
x = x + 20;
}
inner();
}
outer();
let result = x;
마지막으로 이 문제를 살펴봅시다. 전역변수인 x는 10이었지만 outer에서 20으로 바뀝니다. inner함수 또한 x를 x + 20으로 바꿔주는데, 내부에 선언되어 있지 않은 x이므로 outer에서 갖고 올 겁니다. 이미 20으로 바뀐 x가 inner안으로 들어와서는 40으로 바뀌게 되겠네요. 그러니 outer()을 한 결과 전역변수 x는 40으로 값이 도출될 겁니다.
이처럼 서로 유사한 문제들을 통해 전역변수가 바뀌어가는 흐름을 알아보았습니다.
let add = function(x) {
let sum = function(y) {
return x + y;
}
return sum;
}
let foo = add(1);
foo(3);
let total = foo(6);
여기서 total의 값은 무엇일까요?
우선 foo라는 변수에는 add(1)이 들어가겠습니다. foo(6)은 add(6)(1)과 같다고 합니다. foo(3)의 결과값은 4이지만, 이 결과는 마지막 줄에는 아무 영향을 주지 않습니다. 따라서 total의 값은 7이 됩니다.
let multiplyByFive = function() {
return function(y) {
return 5 * y;
}
}
const multiplyBy10 = multiplyByFive(10);
multiplyBy10(4);
multiplyByFive(10) 의 전달인자 10는 multiplyBy10 에 전달되지 않고, 단순히 multiplyBy10(4) 의 전달인자 4와 5를 곱한 20이 리턴값입니다.
...라고 되어 있는데, 이 문제는 틀려서 곤혹스러웠습니다. 10이 저장되지 않기 때문에 4*5의 결과만 나왔습니다.
let multiplyByX = function(x) {
return function(y) {
return x * y;
}
}
const multiplyBy20 = multiplyByX(20);
multiplyBy20(2);
// 다른 문제
let multiplyByFive = function() {
return function(y) {
return 5 * y;
}
}
const multiplyBy10 = multiplyByFive(10);
multiplyBy10(4);
결국 샘플로 나온 두 코드를 놓고 비교해보면서 이해해야 했습니다. 스코프와 클로저에 대한 문제 답습니다. multiplyBy20은 최초에 선언된 어휘적 환경에 multiplyByFive의 매개변수가 포함되어 있기 때문에, 위 문제에서는 답이 40이 됩니다. 하지만 아래 문제에서는 multiplyByFive에 지정된 매개변수가 없기 때문에 multiplyBy10이 10을 기억하고 있지 않는 듯합니다. 배울수록 더 헷갈리는 기분이 드네요.
하지만 클로저의 사용법을 전보다 더 알게 된 것 같습니다.
그래서 클로저는 뭐다?
-> 함수와 그 함수가 접근할 수 있는 변수의 조합이다. 클로저를 활용하면 클로저의 함수 내에 데이터를 보존해두고 사용할 수 있다. 특정 데이터를 다른 함수의 호출로부터 보호하는 데에도 쓰인다.
+) 별로 연관은 없지만, 질문의 중요성에 대한 동기부여 영상이에요. 정확히는 좋은 질문을 하는 자세 말이죠.
누가 추천해주셔서 봤는데 막힘없이 초심자가 알아듣기 쉽게 말하시는 모습이 정말 감명 깊었기에 첨부합니다.
'CODE STATES 44' 카테고리의 다른 글
| fetch API에 promise 활용하기 (0) | 2023.03.21 |
|---|---|
| 주말공부: 구조분해할당 (0) | 2023.03.19 |
| Bees (프로토타입 체인 과제 해설) (0) | 2023.03.16 |
| 객체 지향 프로그래밍 1-4) 프로토타입 체인 (0) | 2023.03.16 |
| 자주 쓰이는 터미널 명령어, Git 명령어 (0) | 2023.03.15 |