220815(월)
🪴 성장일지
책 행복한 이기주의자(웨인 다이어)
의 내용에 자극받아 시작하는 소박한 성장기록
살아있는 꽃과 죽은 꽃은 어떻게 구별하는가?<br/> 성장하고 있는 것이 살아 있는 것이다.<br/> 생명의 유일한 증거는 성장이다!
🌳 키워드 최대한 간단하게 정리, 추후에 보면서 스스로 설명
JavaScript
콜백 함수
다른 코드의 인자로 넘겨주는 함수<br/>
콜백 함수를 넘겨받은 코드는 필요에 따라 적절한 시점에 콜백 함수를 실행(콜백 함수의 제어권
을 넘기는 것)
콜백 함수 예시
var count = 0; var timer = setInterval(function () { console.log(count); if (++count > 4) clearInterval(timer); }, 3000);
일반적인 코드는 사용자(유저)가 호출 주체이면서 제어권을 가지는 반면, 콜백 함수는 특정 코드에게 제어권을 넘겨줌
콜백 함수 예시2
var newArr = [10, 20, 30].map(function (value, index) { console.log(value, index); return value + 5; });
위와 같이 콜백 함수의 종류에 따라 넘겨줘야하는 인자가 제각각<br/>
제어권을 갖는 함수 혹은 메서드가 어떤 인자를 어떻게 받아서 어떻게 활용하는지 꼭 확인해야함<br/>
또한, 일반적으로 thisArg
를 생략할 경우 콜백 함수는 전역 객체(window)를 바인딩
예시1의 setInterval, setTimeout은 애초에 thisArg를 인자로 넘겨줄 수도 없음
콜백 함수 예시3
document.body.innerHTML += '<div id="jay">안녕</div>'; document.body.querySelector('#jay').addEventListener('click', function (event) { console.log(this, event); });
addEventListener의 경우, 자체적으로 this를 그 앞의 호출 주체인 document.body.querySelector('#jay')
으로 바인딩
콜백 함수도 결국 함수
어떤 객체의 메서드로서 콜백 함수를 전달하더라도 그 메서드의 this binding과는 별개로 그저 함수로서 작동
var obj = { arr: [1, 2, 3], method: function (value, index) { console.log(this, value, index); // 메서드로서 호출 시 this는 obj 객체를 바인딩 }, }[(4, 5, 6)].forEach(obj.method); // this는 전역객체를 바인딩(콜백 함수에서 따로 지정하지 않았으므로) // 콜백 함수에 원하는 this 바인딩하기 var arr2 = [4, 5, 6]; arr2.forEach(obj.method.bind(arr2)); // this에 arr2가 바인딩됨
콜백 지옥과 비동기 제어의 변천사
- 콜백 지옥: 콜백 함수를 익명 함수로 전달하는 과정이 반복되며 코드의 들여쓰기가 감당하기 힘들 정도로 깊어지는 현상
- 비동기 제어의 변천사: setTimeout/setInterval -> Promise, Generator -> async/await
🌟🌟🌟 JavaScript
클로저
클로저는 자바스크립트뿐 아니라 함수형 프로그래밍 언어에서 등장하는 보편적인 특성<br/> <strong>어떤 함수 A(외부함수)에서 선언한 변수 a를 참조하는 내부함수 B를 외부로 전달할 경우, A의 실행 컨텍스트가 종료된 이후에도 변수 a가 사라지지 않는 현상</string>
클로저의 정의에서 '내부함수를 외부로 전달할 경우'가 return만 있는 게 아님 setInterval, addEventListener과 같은 비동기 처리 시에도, 콜백 함수가 외부를 참조하는 경우 클로저 발생 콜백 함수는 완전히 종료되는 것이 아닌, 그 함수를 제어하는 함수의 특정 동작(일정 시간마다, 클릭 시, 스크롤 시 등등)에 따라 계속 호출되기 때문에 콜백 함수가 외부 변수를 참조하는 경우 클로저가 발생
// 예시 1번 var outer = function () { var a = 1; var inner = function () { console.log(++a); }; return inner(); }; var outer2 = outer(); console.log(outer2); // 2 // 예시 2번 var outer = function () { var a = 1; var inner = function () { console.log(++a); }; return inner; // 함수를 호출하지 않고 그 함수 자체를 리턴 }; var outer2 = outer(); console.log(outer2()); // 2 console.log(outer2()); // 3
예시 1번: inner가 실행되고 리턴됨으로써 inner의 컨텍스트가 종료되면서 outer의 변수를 참조하는 대상이 없어지기 때문에 가비지 콜렉터에 의해 자연스럽게 메모리에서 처리됨
예시 2번: inner가 실행되지 않고 리턴됨으로써, inner의 컨텍스트(outerEnvironmentReferece)는 outer의 컨텍스트(LexicalEnvironment)를 참조하게 되므로 outer의 변수가 가비지 콜렉터에 의한 제거 대상에 있지 않음
클로저와 메모리
클로저 현상으로 발생하는 메모리 소모는 그저 클로저의 본질적인 특성<br/> 개발자의 의도와 달리 어떤 값의 참조 카운트가 0이 되지 않아서 가비지 콜렉터의 수거 대상이 되지 않는 경우는 메모리 누수라고 할 수 있지만, 클로저는 의도적으로 참조 카운트를 0이 되게 하지 않음으로써 외부 함수의 변수를 활용하는 것이기에 메모리 누수라고 말할 수 없음
그러므로 클로저의 필요성이 끝났다면, 그저 그 메모리를 비워주는 작업을 해주면 해결
var outer = (function () { var a = 1; var inner = function () { console.log(++a); }; return inner; })(); console.log(outer()); console.log(outer()); console.log(outer()); // 이제 클로저를 이용할 일이 없다면 outer = null; // outer 식별자의 inner 함수 참조를 끊어버리면 더이상 inner가 호출될 일이 없고 // 그에 따라 외부함수의 변수 a를 참조할 일이 없게 되므로 가비지 콜렉터에 의해 제거됨
undefined