프로퍼티 연동 방지
- Object에 Object를 할당하면 프로퍼티 값이 연동됨
var origin {member: 100};
var dup = origin;
dup.member = 200;
log(origin.member);
// 200
- 배열도 마찬가지로 연동됨
var origin = [1, 2, 3];
var dup = origin;
dup[1] = 200;
log(origin);
// [1, 200, 3]
- 연동 방지: 프로퍼티 단위로 할당
var origin = {member: 100};
var dup = {};
for (var name in origin){
dup[name] = origin[name];
};
dup.member = 200;
log(origin.member);
log(dup.member);
// 100
// 200
재귀 함수
- Recursive Function
- 함수 안에서 자신 함수를 호출하는 형태
- 사용 사례
- {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]) : log(type + ":", param[type]);
}
};
show(book);
// name:100
// value:200
- show(book);
- 마지막 줄에서 show(book)를 호출하면서 book 오브젝트를 파라미터 값으로 넘겨줌
- for (var type in param){...}
- for-in으로 파라미터로 받은 오브젝트 전개
- typeof param[type] === "object" ? show(param[type]) : web.log(type + ":", param[type]);
- param[type] 타입이 "object"이면
- show()를 호출. 자신을 호출하면서 param[type]을 넘겨줌
- book["memeber"]이므로 {name: 100}이 넘어감
- param[type] 타입이 "object"가 아니면
- member: {name: 100}에서 {name: 100}을 읽은 것이므로 값을 출력
정리 시간
** 데이터 형태 **
var member = {
Jan : {item: {title: "JS북", amount: 100}, point: [10, 20, 30]},
Feb: {item: {title: "JS북", amout: 200}, point: [40, 50, 60]}
}
- 재귀 함수로 데이터를 출력하시오.
- 오브젝트이면 프로퍼티 이름(title, amount)과 값을 출력하고
- 배열이면 값([10, 20, 30])을 출력하고 값을 누적하기
- 재귀 호출이 끝나면 누적한 값 출력하기
즉시 실행 함수
- 함수 즉시 실행이란?
- 엔진이 함수를 만났을 때
- 자동으로 함수를 실행
- 즉시에 실행하므로 즉시 실행 함수
(function(){
log("JS북");
}());
// JS북
- IIFE: Immediately Invoked Function Expression
- (function(){...}()) 형태
- 함수 이름이 없으므로 함수 선언문, 함수 표현식도 아님
- 문법 에러가 발생하지 않음
- 무명 함수, 익명 함수라고도 부름
함수 즉시 실행 과정
- 표현식을 평가
- 소괄호()는 그룹핑 연산자
var total = (1 + 2);
log(total);
// 3
- 함수 이름 필요
- 함수에 이름이 없으면 문법 에러
var value = function(){
return 100;
};
log(value());
// 100
- 함수 표현식 끝에 소괄호 작성
var value = function(){
return 100;
}();
log(value);
// 100
- 소괄호()에 함수 작성
var value = (function(){
return 100;
}());
log(value);
// 100
- 그룹핑 연산자에서 반환된 값이 할당되는 변수를 작성하지 않은 형태
- })()처럼 소괄호를 끝에 작성 가능
(function(){
log(100);
}());
// 100
/*
1. 그룹핑 연산자를 작성하지 않으면 함수 이름이 없으므로 문법 에러
2. 하지만, 그룹핑 연산자를 작성하면 표현식에 function을 작성한 것이므로 문법 에러가 발생하지 않음
즉, (1+2)에서 1+2 대신에 함수를 작성한 것
3. 표현식과 표현식 평가 결과는 평가 결과가 반환될 때까지 메모리에 저장하고 평가 결과를 반환하면 지워짐
4. (1+2)의 결과가 메모리에 저장된다면 매우 많은 메모리가 필요한 것
5. function(){}(); 코드로 만든 오브젝트도 메모리에 저장되지 않으며 실행 결과도 메모리에 저장되지 않음
6. 따라서 저장해야 할 것이 있다면 표현식 밖의 변수, 프로퍼티에 저장해야 함
7. 저장할 필요가 없는 1회성 코드이면서 엔진이 function 키워드를 만나는 시점에 즉시 실행해야 한다면
8. 그룹핑 연산자 안에 표현식으로 작성
9. 무명 함수는 그룹핑 연산자 안의 코드를 한 번만 사용할 때 사용. 주로 초기값을 설정할 때 사용
*/
클로저
- Closure
- function 오브젝트를 생성할 때 함수가 속한 스코프를 [[Scope]]에 설정하고
- 함수가 호출되었을 때 [[Scope]]의 프로퍼티를 사용하는 메커니즘
- [[Scope]]의 설정과 사용 방법을 이해하면 클로저는 단지 논리적인 설명
클로저 논리
실행 콘텍스트: {
렉시컬 환경 컴포넌트: {
환경 레코드: {
선언적 환경 레코드: {}.
오브젝트 환경 레코드: {}
},
외부 렉시컬 환경 참조: {}
}
}
- 실행 중인 function 오브젝트에
- 작성한 변수, 함수를 선언적 환경 레코드에 설정
- [[Scope]]의 변수, 함수를
- 외부 렉시컬 환경 참조에 바인딩
- 변수 이름으로 접근하여 값을 사용하거나 변경할 수 있음
- 함수를 호출할 수 있음
클로저 논리 전개
function book(){
var point = 100;
var getPoint = function(param){
point = point + param;
return point;
};
return getPoint;
};
var obj = book();
log(obj(200));
// 300
- var obj = book();
- book()을 호출하면 엔진은 아래 방법으로 처리
- getPoint()의 클로저가 만들어짐
실행 준비 단계
2. 실행 콘텍스트(EC) 생성
3. 3개의 컴포넌트 생성
- 렉시컬/변수 환경 컴포넌트, this 바인딩 컴포넌트
4. function 오브젝트의 [[Scope]]를
- 외부 렉시컬 환경 참조에 바인딩
<여기까지 모습>
실행 콘텍스트 = {
렉시컬 환경 컴포넌트 = {
환경 레코드: {
선언적 환경 레코드: {},
오브젝트 환경 레코드: {}
},
외부 렉시컬 환경 참조: {[[scope]]}
},
변수 환경 컴포넌트 = {Same}
this 바인딩 컴포넌트: {}
}
초기화 및 실행 단계
5. var point; var getPoint;
- 변수 이름을 선언적 환경 레코드에 설정
6. var point = 100;
- 선언적 환경 레코드의 point에 100 할당
7. var getPoint = function(param){코드};
- function 오브젝트 생성
- 스코프를 [[Scope]]에 바인딩
- point:100이 [[Scope]]에 바인딩됨
getPoint 오브젝트 모습
렉시컬 환경 컴포넌트 = {
환경 레코드: {
선언적 환경 레코드: {}.
},
외부 렉시컬 환경 참조: {
point: 100
}
}
8. return getPoint;
- getPoint function 오브젝트 반환
9. var obj = book();
- return getPoint에서 반환한
- getPoint function 오브젝트를 obj에 할당
10. console.log(obj(200));
- obj()를 호출하면 getPoint(200) 함수가 호출됨
- 클로저와 관련된 부분만 추려보면 아래 처리를 하게 됨
클로저와 관련된 부분
11. 실행 콘텍스트(EC) 생성
- getPoint 오브젝트의 [[Scope]]를 외부 렉시컬 환경 참조에 바인딩
- 파라미터 이름에 값을 매핑하고 결과를 선언적 환경 레코드에 설정
<여기까지 모습>
렉시컬 환경 컴포넌트 = {
환경 레코드: {
선언적 환경 레코드: {
param: 200
},
},
외부 렉시컬 환경 참조: {
point: 100
}
}
12. 함수 안의 코드 실행
13. point = point + param;
- point를 선언적 환경 레코드에서 식별자 해결
- point가 없으므로 외부 렉시컬 환경 참조에서 식별자 해결
- point가 있으며 값이 100
- param을 선언적 환경 레코드에서 식별자 해결
- param이 있으며 값이 200
- 100과 200을 더해 외부 렉시컬 환경 참조의 point에 할당
14. 변수가 선언적 환경 레코드에 없으며 외부 렉시컬 환경 참조에서 식별자 해결
15. 이것이 클로저 논리
클로저와 무명 함수
- 무명 함수 안에 작성한 값, 함수는
- 무명 함수가 끝나면 지워짐
- 따라서 다시 사용하려면 저장 필요
- 한편, 무명 함수는 저장하지 않으려는 의도로 사용
- 클로저 활용
- 클로저는 함수 밖 스코프의 변수와 함수를 사용할 수 있음
- 변수는 외부에서 직접 접근할 수 없으므로 정보 보호
- 무명 함수 안에서 클로저의 변수를 가진 함수를 반환하면 함수의 재사용과 정보 보호를 할 수 있음
var book = (function(){
var point = 100;
function getPoint(param){
return point + param;
};
return getPoint;
}());
log(book(200));
// 300
- function getPoint(param){...}
- [[Scope]]에 스코프 설정
- return getPoint;
- 즉시 실행 함수에서 getPoint 함수 반환
- book 변수에 할당
- point 변수값을 사용할 수 있음
- console.log(book(200));
- 반환된 함수를 호출하면서 200을 파라미터 값으로 넘겨줌
- function getPoint(param){
return point + param;
};- getPoint function 오브젝트의 [[Scope]]에
JS에서 클로저
- 함수에서 함수 밖의 변수 사용은
- JS의 기본 메커니즘
- 논리적 근거는
- 외부 렉시컬 환경 참조에 함수가 속한 스코프가 설정되기 때문
- 클로저는 이를 나타내는 용어
- 용어보다 논리적 구조 이해
'Javascript > [인프런] 자바스크립트 중고급' 카테고리의 다른 글
[자바스크립트 중고급] 6. this (0) | 2022.02.18 |
---|---|
[자바스크립트 중고급] 5. function instance (0) | 2022.02.18 |
[자바스크립트 중고급] 4. Execution Context (0) | 2022.02.18 |
[자바스크립트 중고급] 3. 스코프(Scope) (0) | 2022.02.17 |
[자바스크립트 중고급] 2. Argument (0) | 2022.02.17 |