즉시실행함수
# 일반적인 코드에서 식별자에 접근해서 참조해보자
- var선언
var dog = "무찌";
개발자도구 콘솔에서 dog를 타이핑하면 저렇게 참조값을 가져와준다.
이게 전역변수로 선언된거다
++ let이나 const는?
얘네는 개발자도구 콘솔에서 undefined로 뜬다.
그 이유는 실행컨텍스트에서 선언적환경레코드(declarative enviroment record)에 존재한다고 한다. 이건 아직 뭔지모르겠음.
# 그럼 코드 전체를 즉시실행함수 리터럴로 감싸고 호출해보면?
(function () {
var dog = "무찌";
})();
이렇게 식별자 참조를 할 수가 없게된다.
이렇게 즉시실행 함수로 코드 전체를 감싸면 전역변수생성을 막을 수 있다.
# 모듈패턴
사실 오늘 포스팅은 이게 메인이다..
let counter = (function () {
let num = 0; //private member
return {
increase() {
return num++;
},
showValue() {
console.log("지금값은 " + num);
},
decrease() {
return num--;
},
};
})();
counter.increase();
counter.increase();
counter.increase();
counter.showValue(); //3
counter.decrease();
counter.showValue(); //2
counter이라는 식별자에 즉시실행함수 리터럴의 호출반환값을 할당했다.
이 즉시실행함수는 코드에서 할당시에 즉시 실행되고 객체를 반환한다.
그 후에 이 반환한 객체를 참조하는 식별자 counter에 메서드들을 호출해보면 안에있는 num의 값이 호출할때마다 바뀐다.
이 num은 어디서든 꺼내볼수없는 private member, 즉 캡슐화해서 정보은닉이 된다.
# 뭐가 문젠데?
let counter = (function () {
let num = 0; //private member
return {
increase() {
return num++;
},
showValue() {
console.log("지금값은 " + num);
},
decrease() {
return num--;
},
};
})();
이 선언할당문을 메모리 그림으로 나타내면
먼저 let counter; 선언문이 시작되고 먼저 저 식별자에 바인딩된 값은 undefined이다. 호이스팅때문이다(자주봤어서 그림안그림)
그후에 저 위에 즉시실행함수를 호출하고 저 즉시실행함수에서 return하는 객체를 counter이라는 식별자에 참조에의한 전달로 바인딩한다. 저 객체에는 increase decrease showValue라는 메서드가 들어있다.
저기서 함수 호출시점에서 함수내부에 있는 메모리구조까지 표현을 해보자.
즉시실행함수안에서 선언 할당한 식별자 num은 저 즉시실행함수가 호출 후, 종료되기까지만 메모리내에서 참조할 수 있어야한다.
그래서 저 선언할당문이 실행&종료된 후 메모리구조는 최종적으로
이렇게 생겨야 할것이다.
그러나 저 counter에 바인딩된 객체의 메서드들을 호출해보니 좀 이상하다?
console.log(counter.num); //undefined
counter.increase();
counter.increase();
counter.increase();
counter.showValue(); //3출력
counter.decrease();
counter.showValue(); //2출력
분명히 counter에는 num이 안들어있다. 그래서 undefined가 뜬다.
당연히 메모리에 들어있는 즉시실행함수객체에도 num이라는 데이터또한 안들어있다.
그러나 increase() decrease()메서드를 호출할때마다 num의 값이 이전값에비해 증가,감소한다. 또한 showValue메서드는 명시적으로 num을 콘솔로찍어준다. 분명 이 num을 참조하고 데이터가 메모리에 들어있다는건데?
그럼 저 num에 들어있던 숫자가 메서드 호출될때마다 저장이된다는건데? 메모리구조에는 안보이는데?
이게 내가 혼란스러웠던 부분이다.
# 메서드를 자세히 볼까?
위에 이해가 안된 그림에서 저 counter에 바인딩된 객체를 자세히 써봤다.
그런데 저 객체안에 메서드들은 전부 num을 참조한다? 음 뭔가..보이는것 같은데?
# 코드로 다시 보자.
저 즉시실행함수의 리턴값인 객체리터럴도 이또한 표현식이다.
표현식은 자바스크립트 엔진이 연산후 메모리에 값으로 들어가게 되는것이다.
그럼 다시 함수 호출시점에 생겨난 메모리그림을 보자.
리턴한 객체리터럴도 값으로써 저장되고 이 함수몸체내에 있는 스코프또한 만들며 객체를 메모리에 갖다 넣은거다.
그런데 반환한 객체에서 메서드들이 사라졌어야할 num을 참조하고 있다.
# 그래서 말하고자하는건 뭔데?
저 메모리에 값으로 들어있는 객체의 메서드들은 모두 즉시실행함수에서 선언할당했던 num이란 식별자를 참조한다.
즉 저 객체의 메서드들이 모두 num이란 식별자를 참조하는데 즉시실행함수로 종료가 되었다고 하더라도 여전히 메서드들에서 식별자 num을 참조하기때문에 저 식별자 num의 바인딩은 사라지지 않는다.
메모리는 참조가 더이상 되지 않을때 메모리공간에서 어느순간에 사라진다.
저 반환한 객체의 메서드에서 식별자 num을 참조하고있고, 때문에 즉시실행함수가 종료되도 반환객체의 메서드들이 참조하는 식별자 num의 바인딩은 존재한다.
다만 코드 어디에서도 저 num은 참조할수 없고 counter의 객체에서 처음에 함수정의할때 반환값에 넣은 객체리터럴의 메서드의 동작에서만 저 num을 참조할 수 있다.
저 즉시실행함수 반환값을 그린이유도 저 반환값에 넣은 객체리터럴또한 메모리공간에 연결된걸 표현하고 싶었고, 또한 함수스코프내에서 num을 참조하게 되는것도 설명하고 싶어서 넣은것이다.
# 스코프 테스트
let counter = (function () {
let num = 0;
let bye_num = 12345; //얘는 반환하는 객체의 메서드가 참조하지 않는 식별자라 함수종료후 바인딩 사라짐
return {
increase() {
return num++;
},
showValue() {
console.log("지금값은 " + num);
},
decrease() {
return num--;
},
showThis() {
console.log(this); // this는 이 리턴한 객체표현식이 만든 객체랑 바인딩되어있음.
console.log(this.num); //this는 현재 객체를 가르키고 프로퍼티 num이 없어서 undefined뜸
console.log(num); //얘는 출력함!! 이 num은 이 즉시실행함수의 num과 바인딩 되어있음.
},
};
})();
counter.showThis()
- bye_num이라는 식별자또한 선언,할당했다. 근데 이건 리턴하는 객체의 메서드에서 참조하지 않기때문에 함수호출후 끝나면 바인딩이 사라지게 된다.
- showThis메서드도 같이 넣어서 리턴해봤다.
counter.showThis()메서드로 실행하면 코드처럼 실행이 되는데, this객체는 반환하는 객체와 바인딩되고 이 반환하는 객체의 스코프는 즉시실행함수의 스코프를 기억한다. 왜냐하면 이 반환하는 객체의 메서드들은 모두 상위스코프(함수스코프)의 num을 참조하고 있기 때문이다.
때문에 counter.showThis()메서드를 쓰더라도 이 객체는 이 즉시실행함수의 스코프또한 기억하기때문에 num을 참조하려고 하고 이 식별자 num은 즉시실행함수가 호출&종료되더라도 반환한 객체의 메서드내에서 여전히 바인딩이 되어있다.
# counter의 객체에 새로 num을 참조하는 메서드를 추가해보면?
counter.changeNum = function () {
num = "바껐어!";
};
counter.changeNum();
counter.showValue(); //num을 바꾸지 못했다.
console.log(num); // 전역객체의 프로퍼티로 생겨남. 즉 저 즉시실행함수의 num이랑 바인딩이 안됨.
이렇게 counter의 객체를 참조해서 메서드를 새로 정의해봤다.
사실 이걸 메서드라고 할 수 있는지모르겠는데..(메서드로 인정하는게 저거랑은 좀 달라서..)암튼 저 새로운 함수를 호출했을때 안에있는 num이 다른 메서들이 참조하는 num이랑 같을수 있을까? 테스트를 해봤는데 역시 바꾸지 못했다.
num = '바꼈어'라는 재할당문은 우리가 목표로했던 식별자 num을 참조하지 못하고 전역객체의 프로퍼티로 새로운 num : '바꼈어'를 만들어준다.
때문에 이걸 캡슐화라고 한다고 한단다.. 클로저개념이라는데 여긴 아직 안읽어봐서..
단톡방에 질문만 두시간한듯..
참고로 답변해준분들 말로는 저건 자바스크립트라서 되는거란다. 자바스크립트가 자바스크립트했다고 맹비난을 했다..
하긴 메모리가 사라져야하는데 남아있고 여전히 참조하는거니..
메모리누수? 뭐 힙 스택? 이러더라..
그리고 이걸 클래스문법으로 해결을 할 수 있다고 한다. 사실 클래스는 내가 객체를 오지게 다루는것도아니고해서 실제코드쓰면서 안썼다.. 모달구현할때나 좀 쓰긴했는데 쓰는이유를 모르겠긴 하더라.
애초에 프레임워크쓰면 보안적인 부분은 좀 해결을 해줘서..
---------------------더 작성중 -----------------------
# 즉시실행함수로 전역변수를 막아보자
dom접근
# vue에서 전역변수는 검색이 안되는듯?
# 상수는 스네이크케이스 대문자