1. 재귀 함수, 프로퍼티 연동 방지, 재귀 함수 형태
프로퍼티 연동 방지
•
Object에 Object를 할당하면 프로퍼티 값이 연동됩니다.
⇒ origin 오브젝트를 dup변수에 할당 후 dup.member에 200을 할당하면 origin.member의 값이 연동되어 바뀌는데 오브젝트를 할당하며 값을 공유하기 때문입니다.
var origin = {member: 100};
var dup = origin;
dup.member = 200;
console.log(origin.member);//200
JavaScript
복사
•
배열도 마찬가지로 연동됩니다.
var origin = [1, 2, 3];
var dup = origin;
dup[1] = 200;
console.log(origin);//[1, 200, 3]
JavaScript
복사
•
연동 방지: 프로퍼티 단위로 할당합니다.
var origin = {member: 100};
var dup = {};
for (var name in origin){
dup[name] = origini[name];
};
dup.member = 200;
console.log(origin.member);//100
console.log(dup.member);//200
JavaScript
복사
재귀 함수
Recursive Function: 함수 안에서 자신 함수를 호출하는 형태
사용 사례
•
{name: {name: {name: value}}}
•
[ [ 1, 2 ], [3, 4] , [5, 6] ]
재귀 함수 형태
1.
show(book);
•
마지막 줄에서 show(book)를 호출하며 book 오브젝트를 파라미터 값으로 넘겨줍니다.
2.
for( var type in param){...}
•
for-in으로 파라미터로 받은 오브젝트를 전개합니다.
3.
typeof param[type] === "object"
? show(param[type])
: console.log(type + ":", param[type]);
•
param[type] 타입이 "object"이면 자기자신을 호출(show())해 param[type]을 넘겨줍니다.
•
book["member"]이므로 {name: 100}이 넘어갑니다.
4.
param[type]타입이 "object"가 아니면 member: {name: 100}에서 {name: 100}을 읽은 것이기에 값을 출력합니다.
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);
JavaScript
복사
[실행 결과]
name: 100
value: 200
2. 즉시 실행 함수, 함수 즉시 실행 과정
즉시 실행 함수
•
함수 즉시 실행이란?
◦
엔진이 함수를 만났을 때 자동으로 함수를 실행하는데, 즉시 실행하기에 즉시 실행함수라 합니다.
(function(){
console.log("JS북");
}());
JavaScript
복사
•
IIFE: Immediately Invoked Function Expression
•
(function(){...}())형태
◦
함수 이름이 없으므로 함수 선언문, 함수 표현식도 아닙니다.
◦
문법 에러가 발생하지 않습니다.
◦
무명 함수, 익명함수라고도 부릅니다.
함수 즉시 실행 과정
•
표현식을 평가
◦
소괄호 () 는 그룹핑 연산자입니다.
◦
(1+2) 형태에서 소괄호는 그룹핑 연산자이며 1 + 2는 표현식입니다.
◦
그룹핑 연산자는 소괄호 안의 표현식을 평가하고 평가 결과를 반환합니다.
◦
소괄호와 표현식 평가가 포인트입니다.
var total = (1 + 2);
console.log(total);
JavaScript
복사
•
함수 이름 필요
◦
함수에 이름이 없으면 문법에러가 발생합니다.
◦
함수 표현식으로 엔진이 function 키워드를 만나면 function오브젝트를 생성하여 value 변수에 할당합니다.
◦
value 변수를 선언하지 않으면 함수 이름이 없기에 문법에러가 발생합니다.
함수 표현식도, 함수 선언문도 아니기 때문입니다.
◦
value()처럼 function 끝에 소괄호를 첨부하면 함수로 호출합니다. 이때, 소괄호()는 그룹핑 연산자가 아닌 함수 호출입니다.
var value = function(){
return 100;
};
console.log(value());
JavaScript
복사
•
함수 표현식 끝에 소괄호 작성
◦
function 키워드를 만나 function 오브젝트를 생성합니다.
◦
소괄호가 있으므로 함수를 호출합니다.
◦
함수에서 반환한 100을 value 변수에 할당합니다.
var value = function(){
return 100;
}();
console.log(value);
JavaScript
복사
•
소괄호()에 함수 작성
◦
소괄호는 그룹핑 연산자이기에 소괄호 안의 표현식을 평가합니다.
◦
표현식이 함수이므로 function 오브젝트를 생성합니다.
◦
function 끝에 소괄호가 있으므로 함수를 실행합니다.
◦
함수 실행결과값 100을 value에 할당합니다.
var value = (function(){
return 100;
}());
console.log(value);
JavaScript
복사
•
그룹핑 연산자에서 반환된 값이 할당되는 변수를 작성하지 않은 형태
1.
그룹핑 연산자를 작성하지 않으면 함수 이름이 없어 문법에러가 납니다.
2.
그룹핑 연산자를 작성하면 표현식에 function을 작성한 것이기에 문법에러 발생안함.
즉, (1 + 2)에서 1+2 대신 함수를 작성한 것입니다.
3.
표현식과 표현식 평가 결과는 평가 결과가 반환할 때까지 메모리에 저장하고 평가 결과를 반환하면 지워집니다.
4.
(1+2)의 결과가 메모리에 저장된다면 메모리 낭비입니다.
5.
function(){}();
코드로 만든 오브젝트도 메모리에 저장되지 않으며(key가 없음) 실행결과도 메모리에 저장되지 않습니다.
6.
그렇기에 저장해야한다면 표현식 밖에 변수, 프로퍼티에 저장해야 합니다.
7.
저장할 필요가 없는 1회성 코드이며 엔진이 function 키워드를 만나는 시점에 즉시 실행해야 한다면 그룹핑 연산자 안에 표현식으로 작성합니다.
8.
무명 함수는 그룹핑 연산자 안의 코드를 한 번만 사용할 때 사용합니다.
주로 초기값을 설정할 때 사용합니다.
(function(){
console.log(100);
}());
JavaScript
복사
3. 클로저(Closure)논리, 클로저 논리 전개
클로저
•
Closure
◦
function 오브젝트를 생성할 때 함수가 속한 스코프를 [[Scope]]에 설정하고 함수가 호출되었을 때 [[Scope]]의 프로퍼티를 사용하는 메커니즘입니다.
•
[[Scope]]의 설정과 사용방법을 이해하면 클로저는 단지 논리적인 설명입니다.
논리
외부 렉시컬 환경 참조에 선언된 변수, 함수를 내것처럼 사용하는 것
•
실행중인 function 오브젝트에 작성한 변수, 함수를 선언적 환경 레코드에 설정합니다.
•
[[Scope]]의 변수 , 함수를 외부 렉시컬 환경 참조에 바인딩합니다.
•
변수 이름으로 접근하여 값을 사용하거나 변경할 수 있습니다.
•
함수를 호출할 수 있습니다.
실행 콘텍스트:{
렉시컬 환경 컴포넌트:{
환경 레코드:{
선언적 환경 레코드: {},
오브젝트 환경 레코드:{}
},
외부 렉시컬 환경 참조:{}
}
}
JavaScript
복사
논리 전개
1.
var obj = book();
•
book()을 호출하면 엔진은 아래 방법으로 처리합니다.
•
getPoint()의 클로저가 만들어집니다.
—실행 준비 단계—
2. 실행 콘텍스트(EC) 생성
3. 3개의 컴포넌트 생성
•
렉시컬/변수 환경 컴포넌트, this바인딩 컴포넌트
4. function 오브젝트의 [[Scope]]를 외부 렉시컬 환경 참조에 바인딩
여기까지의 모습
실행 콘텍스트:{
렉시컬 환경 컴포넌트:{
환경 레코드:{
선언적 환경 레코드: {},
오브젝트 환경 레코드:{}
},
외부 렉시컬 환경 참조:{[[scope]]}
},
변수 환경 컴포넌트 = {Same}
this 바인딩 컴포넌트: {}
}
JavaScript
복사
—초기화 및 실행 단계—
5. var point; var getPoint;
•
변수 이름을 선언적 환경 레코드에 설정합니다.
6. var point = 100;
•
선언적 환경 레코드의 point에 100을 할당합니다.
7. var getPoint = function(param){코드};
•
function 오브젝트 생성
•
스코프를 [[Scope]]에 바인딩
•
point:100이 [[Scope]]에 바인딩 됩니다.
getPoint 오브젝트의 모습
렉시컬 환경 컴포넌트 = {
환경 레코드: {
선언적 환경 레코드 : {},
},
외부 렉시컬 환경 참조: {
point: 100
}
},
JavaScript
복사
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
}
},
JavaScript
복사
function book(){
var point = 100;
var getPoint = function(param){
point = point + param;
return point;
};
return getPoint;
};
var obj = book();
console.log(obj(200));
JavaScript
복사
12. 함수 안의 코드 실행
13. point = point + param:
•
point를 선언적 환경 레코드에서 식별자 해결
◦
point가 없기에 외부 렉시컬 환경 참조에서 식별자 해결
◦
point가 있으며 값은 100입니다.
•
param을 선언적 환경 레코드에서 식별자 해결
◦
param이 있으며 값이 200입니다.
•
100과 200을 더해 외부 렉시컬 환경 참조의 point에 할당
14. 변수가 선언적 환경 레코드에 없으면 외부 렉시컬 환경 참조에서 식별자 해결
결론: 내부에서 외부 렉시컬 환경참조 [[Scope]]에 있는 변수, 함수를 자기것처럼 사용하는 것
4. 클로저와 무명 함수, JS에서 클로저
클로저와 무명 함수
•
무명 함수 안에 작성한 값, 함수는 무명 함수가 끝나면 지워집니다. 그렇기에 다시 사용하려면 저장이 필요합니다.
•
한편, 무명 함수는 저장하지 않으려는 의도로 사용합니다.
•
클로저 활용
◦
클로저는 함수 밖 스코프의 변수와 함수를 사용할 수 있습니다.
◦
변수는 외부에서 직접 접근할 수 없으므로 정보 보호
◦
무명 함수 안에서 클로저의 변수를 가진 함수를 반환하면 함수의 재사용과 정보 보호를 할 수 있습니다.
코드를 통한 분석
var book = (function(){
var point = 100;
function getPoint(param){
return point + param;
};
return getPoint;
}());
console.log(book(200));
JavaScript
복사
•
function getPoint(param){...}
◦
[[Scope]]에 스코프 설정
•
return getPoint;
◦
즉시 실행 함수에서 getPoint 함수를 반환합니다.
◦
book 변수에 할당합니다.
◦
point 변수값을 사용할 수 있습니다.
•
console.log(book(200));
◦
반환된 함수를 호출하며 200을 파라미터 값으로 넘겨줍니다.
•
function getPoint(param){
return point + param;
};
◦
getPoint function 오브젝트의 [[Scope]]에 point 사용
JS에서 클로저
함수에서 함수 밖의 변수 사용은 JS의 기본 메커니즘입니다. 이는 외부 렉시컬 환경 참조에 함수가 속한 스코프가 설정되기 때문인데 클로저는 이를 나타내는 용어로 용어 자체보다는 논리적 구조를 이해하는것이 중요합니다.