재귀 함수
- 프로퍼티 연동 방지
- Object에 Object를 할당하면 프로퍼티 값이 연동된다.
var origin = {member: 100}; var dup = origin; dup.member = 200; console.log(origin.member); //200;
- origin 오브젝트를 dup 변수에 할당한 후 dup.member에 200을 설정하면 origin.member 값이 연동되어 바뀐다.
- 오브젝트를 할당하면 값을 공유하기 때문이다.
- 배열도 마찬가지로 연동된다.
var origin = [1, 2, 3]; var dup = origin; dup[1] = 200; console.log(origin); // [1, 200, 3]
- 배열도 마찬가지로 배열을 할당하면 값을 공유한다.
- 연동방지: 프로퍼티 단위로 할당
var origin = {member: 100}; var dup = {}; for(var name in origin) { dup[name] = origin[name]; } dup.member = 200; console.log(origin.member); // 100 console.log(dup.member); // 200 `` ```javascript var origin = [1, 2, 3]; var dup = [] for(var i = 0; i < origin.length; i++) { dup[i] = origin[i]; } dup[1] = 200; console.log(origin); // [1, 2, 3] console.log(dup); // [1, 200, 3]
- 값의 공유를 방지하려면 프로퍼티 단위로 할당해야 한다.
- Object에 Object를 할당하면 프로퍼티 값이 연동된다.
- 재귀함수
- 함수 안에서 자신 함수를 호출하는 형태
- 사용사례
- {name: {name: {name: value} } }
- [ [1, 2], [3, 4], [5, 6] ]
- 재귀 함수 형태
var book = { member: {name: 100}, point: {value: 200} }; function show(param) { for(var type in param) { typeof param[type] === "object" ? show(param[type]) : console.log(type + " : " + param[type]); } } show(book); // name : 100 / value : 200
- show(book);
- 마지막 줄에서 show(book)을 호출하면서 book 오브젝트를 파라미터 값으로 넘겨준다.
- for(var type in param) {…}
- fon-in으로 파라미터로 받은 오브젝트 전개
- typeof param[type] === “object” ? show(param[type]) : console.log(type + “ : “ + param[type]);
- value가 object 타입이면, 다시 show 함수 호출 아니면 key, value를 로그로 찍는다.
- book[“member”], book[“point”] 의 value가 오브젝트이므로 다시 재귀 호출
- member[“name”] 은 object가 아니므로 name : 100 을 로그로 찍는다.
- member[“value”] 는 object가 아니므로 value : 200을 로그로 찍는다.
- show(book);
즉시 실행 함수
- 즉시
- 엔진이 함수를 만났을 때 자동으로 함수를 실행. 즉시에 실행하므로 즉시 실행 함수
(function() { console.log("JS북"); } ());
- IIFE: Immediately Invoked Function Expression
- (function() {…}()) 형태
- 함수 이름이 없으므로 함수 선언문, 함수 표현식도 아니다.
- 문법 에러가 발생하지 않는다.
- 무명함수, 익명함수라고도 부른다.
- 함수 즉시 실행 과정
- 표현식을 평가 소괄호()는 그룹핑 연산자
var total = (1 + 2); console.log(total); // 3
- (1 + 2) 형태에서 소괄호()는 그룹핑 연산자이며 1 + 2 는 표현식
- 그룹핑 연산자는 소괄호 안의 표현식을 평가하고 평가 결과를 반환
- 소괄호()와 표현식 평가가 키포인트
- 함수 이름 필요
- 함수에 이름이 없으면 문법 에러
var value = function() { return 100; }; console.log(value()); // 100
- 함수 표현식으로 엔진이 function 키워드를 만나면 function object를 생성하여 value 변수에 할당
- value 변수를 선언하지 않으면 함수 이름이 없으므로 문법 에러, 함수 표현식도, 함수 선언문도 아니기 때문이다.
- value()처럼 function 끝에 붙는 소괄호는 함수 호출 용도(그룹핑이 아니다)
- 함수에 이름이 없으면 문법 에러
- 함수 표현식 끝에 소괄호 작성
var value = function() { return 100; }(); console.log(value); // 100
- 함수 끝에 소괄호를 첨부한 형태
- function 키워드를 만나 function object 생성
- 소괄호가 있으므로 함수 호출
- 함수에서 반환한 100을 변수 value에 할당
- 소괄호 안에 함수 작성
var value = (function() { return 100; }()); console.log(value); // 100
- 소괄호 안에 함수 작성
- 소괄호는 그룹핑 연산자
- 그룹핑 연산자이므로 소괄호 안의 표현식을 평가
- 표현식이 함수이므로 function object 생성
- function 끝에 소괄호가 있으므로 함수 실행
- 그룹핑 연산자에서 반환된 값이 할당되는 변수를 작성하지 않은 상태
(function() { console.log(100); }()); // 100
- 그룹핑 연산자를 작성하지 않으면 함수 이름이 없으므로 문법 에러 발생
- 하지만, 그룹핑 연산자를 작성하면 표현식에 function을 작성한 것이므로 문법 에러가 발생하지 않는다. 즉, (1+2)에서 1 + 2 대신에 함수를 작성한 것
- 표현식과 표현식 평가 결과는 평가 결과가 반환할 때까지 메모리에 저장하고 평가 결과를 반환하면 지워진다.
- ( 1 + 2 )의 결과가 메모리에 저장된다면 매우 많은 메모리가 필요할 것이다.
- function(){}();
- 코드로 만든 오브젝트도 메모리에 저장되지 않으며 실행 결과도 메모리에 저장되지 않는다.
- 따라서 저장해야 할 것이 있다면 표현식 밖의 변수, 프로퍼티에 저장해야 한다.
- 저장할 필요가 없는 1회성 코드이면서 엔진이 function 키워드를 만나는 시점에 즉시 실행해야 한다면 그룹핑 연산자 안에 표현식으로 작성
- 무명 함수는 그룹핑 연산자 안의 코드를 한번만 사용할 때 사용. 주로 초깃값 설정할 때 사용
- 표현식을 평가 소괄호()는 그룹핑 연산자
Closure
- Closure
- function object를 생성할 때 함수가 속한 스코프를 [[Scope]]에 설정하고
- 함수가 호출되었을 때 [[Scope]]의 프로퍼티를 사용하는 메커니즘
- [[Scope]]의 설정과 사용방법을 이해하면 클로저는 단지 논리적인 설명
- Closure 논리
실행 콘텍스트(EC): { 렉시컬 환경 컴포넌트(LEC): { 환경 레코드(ER): { 선언적 환경 레코드(DER): {}, 오브젝트 환경 레코드(OER): {} }, 외부 렉시컬 환경 참조(OLER): {} }, ... }
- 실행중인 function object에 작성한 변수, 함수를 선언적 환경 레코드(DER)에 설정
- [[Scope]]의 변수, 함수를
- 외부 렉시컬 환경 참조(OLER)에 바인딩
- 변수 이름으로 접근하여 값을 사용하거나 변경할 수 있음
- 함수로 호출할 수 있음
- Closure 논리는 외부 렉시컬 환경 참조(OLER)에 있는 변수와 함수를 내것처럼 사용하는 것이다.
function book() { var point = 100; var getPoint = function(param) { point = point + param; return point; } return getPoint; }; var obj = book(); console.log(obj(200)); // 300
- var obj = book();
- book()함수를 호출하면 엔진은 아래 방법으로 처리
- getPoint()의 클로저가 만들어 진다.
- 실행 콘텍스트(EC) 생성
- 3개의 컴포넌트 생성
- LEC, VEC, TBC
- function object의 [[Scope]]를 외부 렉시컬 환경 참조에 바인딩
- 초기화 단계
- var point, var getPoint
- 변수 선언적 환경 레코드(DER)에 설정. undefined 할당
- var point = 100;
- DER 속 point에 100 할당
- var getPoint = function(param) { … };
- function object 생성
- 스코프를 [[Scope]]에 바인딩
- point: 100이 [[Scope]]에 바인딩된다. - getPoint function의 EC에 외부 렉시컬 환경 참조(OLER)에 바인딩
- return getPoint;
- getPoint function object 반환
- var obj = book();
- return getPoint() 반환
- getPoint function 오브젝트롤 obj에 할당
- console.log(obj(200));
- obj()를 호출하면 getPoint(200) 함수가 호출된다.
- 클로저와 관련된 부분만 추려보면 아래 처리를 하게 된다.
- 클로저와 관련된 부분
- 실행 콘텍스트(EC) 생성
- getPoint 오브젝트의 [[Scope]]를 외부 렉시컬 환경 참조(OLER)에 바인딩
- 파라미터 일므에 값을 매핑하고 결과를 선언적 환경 레코드(DER)에 설정
실행 콘텍스트(EC): { 렉시컬 환경 컴포넌트(LEC): { 환경 레코드(ER): { 선언적 환경 레코드(DER): { param: 200 }, 오브젝트 환경 레코드(OER): {} }, 외부 렉시컬 환경 참조(OLER): { point: 100 } }, ... }
- 함수 안의 코드 실행
- point = point + param;
- point를 선언적 환경 레코드에서 식별자 해결
- point가 없으므로 외부 렉시컬 환경 참조에서 식별자 해결
- point가 있으며 값이 100
- param을 선언적 환경 레코드에서 식별자 해결
- param이 있으며 값은 200
- 100과 200을 더해 외부 렉시컬 환경 참조의 point에 할당
- point를 선언적 환경 레코드에서 식별자 해결
- 변수가 선언적 환경 레코드에 없으면 외부 렉시컬 환경 참조에서 식별자 해결
- 실행 콘텍스트(EC) 생성
- var obj = book();
- 엔진이 함수를 만났을 때 자동으로 함수를 실행. 즉시에 실행하므로 즉시 실행 함수
-
클로저와 무명 함수
- 무명함수 안에 작성한 값, 함수는 무명함수가 끝아면 지워진다. 따라서 다시 사용하려면 저장이 필요하다. 한편, 무명함수는 저장하지 않으려는 의도로 사용
- 클로저 활용
- 클로저는 함수 밖 스코프의 변수와 함수를 사용할 수 있다.
- 변수는 외부에서 직접 접근할 수 없으므로 정보를 보호
- 무명 함수 안에서 클로저의 변수를 가진 함수를 반환하면 함수의 재사용과 정보 보호를 할 수 있다.
- 예제
var book = (function(){ var point = 100; function getPoint(param) { return point + param }; return getPoint; }()); console.log(book(200)); // 300
- function getPoint(param) {…}
- [[Scope]]에 스코프 설정
- return getPoint;
- 즉시 실행 함수에서 getPoint 함수 반환하여 book 변수에 할당
- point 변수값을 사용할 수 있다.
- console.log(book(200));
- 반환된 getPoint function object를 호출하면서 200을 파라미터로 넘겨준다.
- function getPoint(param) { … }
- getPoint function object의 [[Scope]] (외부 렉시컬 환경 참조(OLER)) 에 있는 point 100을 가져올 수 있다.
- param은 선언적 환경 레코드(DER)에 있는 값을 가져올 수 있다.
- function getPoint(param) {…}
- Javascript에서 Closure
- 함수에서 함수 밖의 변수 사용은 JS의 기본 메커니즘
- 논리적 근거는 외부 렉시컬 환경 참조(OLER)에 함수가 속한 스코프가 설정되기 때문
- 클로저는 이를 나타내는 용어로 용어보다는 논리적인 구조 이해가 필요
** 출처1. 인프런 강좌_자바스크립트 중고급: 근본 핵심 이해