lodash 메서드 구현 예제를 풀어보면서
아직도 closure에 대한 개념이 부족하다는 것을 많이 깨달았다 😂
특히 return 문을 언제 어떻게 써야 하는 가에 대해 정리가 너무 안됐었는데 return으로 검색해도 원하는 답을 찾을 수 없었다.
그리고 왜 없는 지도 이제 알았다. 너-무 기본이라 그랬던 것이다.
정말 바보다..나는 바.. 보..
클로저는 실행컨텍스트가 열렸을 때, 자신을 감싸고 있는 lexical scope 내부에서 선언된 변수 또한 참조할 수 있는 현상이다.
비록 자신을 감싸고 있던 함수가 이미 실행되어 종료된 후라 할지라도 말이다.
클로저를 구현하기 위해, 함수를 실행하는 방법은 크게 2가지로 나뉘는데,
바로 함수를 실행하는 방법과 변수에 담아서 실행하는 방법이다.
😐 예제_basic
var outer = function () {
var a = 1;
var inner = function () {
console.log(++a); // 2
};
inner();
};
outer();
⏩ outer()
위와 같이 바로 outer 함수를 실행할 경우,
inner()
실행문으로 인해 inner함수의 컨텍스트가 열리고,
변수 a를 참조할 수 있다.
만약 이때 아래와 같이 구조를 바꾼다면?
(return만 쓰면 되는 줄 알았을 때 이런 바보같은 실수를 했었다..)
var outer = function () {
var a = 1;
return function () {
console.log(++a); // 아무것도 없었다고 한다.
};
};
outer();
이렇게 작성한다면 익명함수 내에서 콘솔에는 a가 찍히지 않는다.
클로저는 함수 선언 기준으로 생성되고 상위스코프 값을 참조하는 건 맞지만,
이 경우에는 리턴하고 끝내라 했으니까 콘솔은 a의 변수를 뱉어낼 틈도 없다.
console.log(outer()); // ƒ () { console.log(++a); }
익명함수를 리턴할 뿐이다.
콘솔한테 왜 a 안 찍냐고 혼내면, 콘솔은 니가 그럴 시간을 줬냐?? 하면서 억울해할 수 밖에..
그렇다면 그놈의 return function은 언제 쓰는 것인가 ??!!!
함수를 변수에 담고 실행하려고 하니까!
즉 함수를 리턴해야 하니return fucntion
이어야지 ㅋㅋ 멍충아.
😬 예제 return_1
var outer2 = function () {
var a = 1;
var inner1 = function () {
return ++a;
};
return inner1;
};
var outerCall = outer2();
console.log(outerCall()); // 2
⏩ var outerCall = outer2();
outerCall()
이번에는 outer2함수의 실행결과함수
를 변수 outerCall에 할당하고
그 outerCall을 실행한 코드이다. 여기서 outerCall은 함수
outer2를 실행하면, 컨텍스트가 열리고 inner1함수를 리턴하면서 종료된다.
이때 생성된 inner1은 상위스코프에 있는 a를 기억한 채로 outerCall에 담긴다.
그리고 outerCall을 다시 실행해줄 때 비로소 기억하고 있던
상위 스코프의 a를 참조하여 ++a 를 뱉어내는 것이다.
그래서 이때는
return 함수표현식 + 함수표현식 내부 return ,
return 함수선언문 + 함수선언문 내부 return 의 콜라보가 되겠다.
😬 예제 return_2
var outer2 = function () {
var a = 1;
return function () {
return ++a;
};
};
var outerCall = outer2();
console.log(outerCall());
⏩ var outerCall = outer2();
outerCall()
이번에도 역시 outer2함수의 실행결과함수
를 변수 outerCall에 할당하고 그 outerCall을 실행한 코드이다. 그런데 앞의 예제보다 산뜻한 코드임을 알 수 있다.
outer2 함수 내부에 익명함수function () {}
구조로 들어있다.
내부함수에서는 return ++a로 외부 변수값을 뱉어내고 있는 건 똑같은데,
이 내부함수를 실행하거나 return 하는 부분이 따로 없다.
대신 익명함수 자체를 리턴하게 만들어져 있다.
이렇게 하는 경우에도 역시 클로저가 형성된다.
단 익명함수의 인자에 a를 넣어줄 필요는 없다.
바로 return 익명함수 + 익명함수 내부 return 의 콜라보이다.
_.flatten을 구현할 때 바보같은 실수의 정체성을 알게 되었다.
그 전까지는 3가지 방법을 돌려가며...썼더랬지.
위에서 설명한 예제1 토대로 다르게 구현해보면 다음과 같다.
😐 예제_basic
_.flatten = function (nestedArray) {
const flattedArray = [];
function flatArray (nestedArray, flattedItems) {
_.each(nestedArray, function (item) {
if (Array.isArray(item)) {
flatArray(item, flattedItems);
} else {
flattedItems.push(item);
}
});
}
flatArray(nestedArray, flattedArray);
return flattedArray;
}
_.flatten(nestedArray);
함수를 리턴하는 것이 아니기 때문에 안에서 flateArray를 실행해준 것.
예제 1번 같은 상황으로 풀어보고 싶어서 위와 같이 만들었지만, 사실
함수를 중첩하지 않고 간결하게 할 수도 있다.
_.flatten = function (nestedArray, flattedArray) {
flattedArray = flattedArray || [];
_.each(nestedArray, function (item) {
if (Array.isArray(item)) {
return _.flatten(item, flattedArray);
} else {
flattedArray.push(item);
}
});
return flattedArray;
}
_.flatten(nestedArray);
내가 flatten을 하면서 제일 애먹었던 부분이 재귀를 돌릴 때마다
flattenArray 가 []로 리셋되는 것이었는데, 이것은 함수를 호출할 때 인자로 추가해주면 해결된다.
1번 방법에서는 바깥 함수의 인자를 추가하는 등의 수정을 하고 싶지 않아서 내부에 flatArray라는 함수를 만들어서 전달하는 방식으로 작성했다.
2번 방법처럼 한다면 재귀를 돌릴때 _.flatten 의 두번째 파라미터로 전달하면 되고,
상단에 flattedArray = flattedArray || [];
로 선언해주면 된다.
재귀돌릴 당시의 배열로 받을 경우= flattedArray
초기값으로 빈배열로 선언할 경우|| [];
로
구분지어 주면 원하는 대로 배열을 셋팅할 수 있다.
_.flatten = function (nestedArray, flattedArray = []) {
_.each(nestedArray, function (item) {
if (Array.isArray(item)) {
return _.flatten(item, flattedArray);
} else {
flattedArray.push(item);
}
});
return flattedArray;
}
_.flatten(nestedArray);
또는 처음부터 파라미터에 flattedArray = []
default 해줄 수도 있다.
특정 값이 아니라면 기본값 []가 내부에 선언될 것이다.
(호이스팅을 떠올리면 인자도 내부에서 값으로 사용된다.)
즉 이부분을 정리하면
① 함수 내부 ⏩
if문
_.flatten = function (nestedArray, flattedArray) { if (!flattedAray) { flattedArray = [] }
② 함수내부 ⏩
or
,||
연산자 이용_.flatten = function (nestedArray, flattedArray) { flattedArray = flattedArray || [];
1, 2 경우에는 let const 등을 사용하지 않아도 된다.
③ 함수 파라미터 ⏩ default ->
array = []
_.flatten = function (nestedArray, flattedArray = [] ) {