재귀와 스택
재귀의 베이스(base)
재귀 단계(recursive step)
재귀 깊이(recursion depth) : 가장 처음 하는 호출을 포함한 중첩 호출의 최대 개수
실행 중인 함수의 실행 절차에 대한 정보 : 실행 컨텍스트(execution context)에 저장.
실행 컨텍스트 : 함수에 대한 세부 정보를 담고 있는 내부 데이터 구조. 제어 흐름의 현재 위치, 변수의 현재 값, this의 값 등이 저장됨.
재귀(recursion) - 함수 내부에서 자기 자신을 호출하는 것을 나타내는 프로그래밍 용어, 함수가 자신을 호출하는 단계를 재귀단계(recursion step)라고 부릅니다. basis라고 불리는 재귀의 베이스(base)는 작업을 아주 간단하게 만들어서 함수가 더 이상은 서브 호출을 만들지 않게 해주는 인수입니다.
나머지 매개변수와 전개 문법
function sumAll(...args) {
let sum = 0;
for(let arg of args) sum += arg;
return sum;
}
console.log(sumAll(1)); // 1
console.log(sumAll(1, 2)); // 3
console.log(sumAll(1, 2, 3)); // 6
spread
alert(Math.max(3, 5, 1)); // 5
let arr = [3, 5, 1];
alert(Math.max(arr)); // NaN
alert(Math.max(...arr)); // 5
전개문법이 배열을 인수 목록으로 변경해줌.
변수의 유효범위와 클로저
코드블록{...} 안에서 선언한 변수는 블록 안에서만 사용할 수 있습니다.
중첩함수 : 함수 내부에서 선언한 함수는 '중첩(nested)' 함수.
렉시컬 환경
- 변수
자바스크립트에선 실행 중인 함수, 코드블록 {...}, 스크립트 전체는 렉시컬 환경(Lexical Environment) 이라 불리는 내부 숨김 연관 객체(internal hidden associated object)를 갖습니다.
- 환경 레코드(environment record) : 모든 지역 변수를 프로퍼티로 저장하고 있는 객체입니다. this 값과 같은 기타 정보도 여기에 저장됩니다.
- 외부 렉시컬 환경(Outer Lexical Environment) 에 대한 참조 - 외부 코드와 연관됨.
'변수'는 특수 내부 객체인 환경 레코드의 프로퍼티일 뿐입니다. '변수를 가져오거나 변경'하는 것은 '환경 레코드의 프로퍼티를 가져오거나 변경'함을 의미.
//execution start // => phrase : <uninitialized>
let phrase; // => phrase : undefined
phrase = "Hello"; // => phares : "Hello"
phrase = "Bye"; // => phrase : "Bye"
- 함수 선언문
함수 선언문으로 선언한 함수는 일반 변수와는 달리 바로 초기화 된다는 점에서 차이가 있음.
let say = function(name)... 함수 표현식은 해당되지 않음.
//execution start // => phrase: <uninitialized>
// say : function
let phrase = "Hello";
function say(name) {
}
- 내부와 외부 렉시컬 환경
함수를 호출해 실행하면 새로운 렉시컬 환경이 자동으로 만들어집니다. 이 렉시컬 환경엔 함수 호출 시 넘겨받은 매개변수와 함수의 지역 변수가 저장됩니다.
let phrase = "Hello";
function say(name) {
alert(`${phrase}, ${name}`);
}
say("kim"); //"Hello, kim"
- 내부 렉시컬 환경은 현재 실행 중인 함수인 say에 상응. 내부 렉시컬 환경엔 함수의 인자인 name으로부터 유래한 프로퍼티 하나만 있음. name = kim
- 외부 렉시컬 환경은 전역 렉시컬 환경, 전역 렉시컬 환경은 phrase와 함수 say를 프로퍼티로 갖음.
- 내부 렉시컬 환경은 외부 렉시컬 환경에 대한 참조를 갖음.
- 코드에서 변수에 접근할 땐 먼저 내부 렉시컬 환경을 검색 범위로 잡음 -> 못찾은 경우 내부 렉시컬 환경이 참조하는 외부 렉시컬 환경으로 확장함.
- 반환함수
- 모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억한다는 점.
- 함수는 [[Environment]] 라 불리는 숨김 프로퍼티를 갖는데, 여기에 함수가 만들어진 곳의 렉시컬 환경에 대한 참조가 저장.
- 변수값 갱신은 변수가 저장된 렉시컬 환경에서 이뤄집니다.
클로저(closure)
클로저는 외부 변수를 기억하고 이 외부 변수에 접근할 수 있는 함수를 의미.
자바스크립트의 함수는 숨김 프로퍼티인 [[Environment]] 를 이용해 자신이 어디서 만들어졌는지를 기억합니다.
함수의 내부 코드는 [[Environment]]를 사용해 외부 변수에 접근합니다.
가비지 컬렉션
함수 호출이 끝나면 함수에 대응하는 렉시컬 환경이 메모리에서 제거.
호출이 끝난 후에도 여전히 도달 가능한 중첩 함수가 있을 수 있습니다. 이때는 이 중첩함수의 [[Environment]] 프로퍼티에 외부 함수 렉시컬 환경에 대한 정보가 저장됩니다. 도달 가능한 상태가 되는 것.
함수 호출은 끝났지만 렉시컬 환경이 메모리에 유지되는 이유는 바로 이 때문.
function f() {
let value = 123;
return function() {
alert(value);
}
}
let g = f(); // g.[[Environment]]에 f() 호출 시 만들어지는 렉시컬 환경 정보가 저장.
function makeArmy() {
let shooters = [];
let i = 0;
while (i < 10) {
let shooter = function() { // shooter 함수
alert( i ); // 몇 번째 shooter인지 출력해줘야 함
};
shooters.push(shooter);
i++;
}
return shooters;
}
let army = makeArmy();
army[0](); // 0번째 shooter가 10을 출력함
army[5](); // 5번째 shooter 역시 10을 출력함
// 모든 shooter가 자신의 번호 대신 10을 출력하고 있음
오래된 var
var는 블록 스코프가 없습니다. 함수 스코프이거나 전역 스코프입니다.
변수가 끌어올려 지는 현상을 호이스팅(hoisting)
var 로 선언한 모든 변수는 함수의 최상위로 끌어 올려지기 때문.
선언은 호이스팅이 되지만 할당은 호이스팅 되지 않습니다.
전역 객체
Window. globalThis
alert("Hello");
window.alert("Hello");
브라우저에서 let이나 const가 아닌 var로 선언한 전역 함수나 전역 변수는 전역 객체의 프로퍼티가 됩니다.
var gVar = 5;
alert(window.gVar); // 5
let gLet = 5;
alert(window.gLet); // undifined
객체로서의 함수와 기명 함수 표현식
함수는 호출이 가능한(callable) 행동 객체
- name 프로퍼티 : 함수의 이름이 저장.
function sayHi() {
alert("hi");
}
alert(sayHi.name); // sayHi
- length 프로퍼티 : 함수 매개변수의 개수를 반환. 나머지 매개변수는 포함안됨.
- 프로퍼티는 변수가 아닙니다.
기명 함수 표현식(Named Function Expression, NFE)
let sayHi = function func(who) {
alert (`Hello, ${who}`);
};
- 이름을 사용해 함수 표현식 내부에서 자기 자신을 참조할 수 있음.
- 기명 함수 표현식 외부에선 그 이름을 사용할 수 없음.
'new Function' 문법
let func = new Function([arg1, arg2,...agrN], functionBody);
함수 표현식과 함수 선언문은 개발자가 직접 스크립트를 작성해야만 함수를 만들 수 있음.
new Function이라는 문법을 사용하면 어떤 문자열도 함수로 바꿀 수 있음. 서버에서 전달받은 문자열을 이용해 새로운 함수를 만들고 이를 실행하는 것도 가능.
let str = ...서버에서 동적으로 전달받은 문자열(코드 형태)...
let func = new Function(str);
func();
setTimeout, setInterval을 이용한 호출 스케줄링
일정 시간이 지난 후에 원하는 함수를 예약 실행(호출)할 수 있게 하는 것을 호출 스케줄링(scheduling a call)
- setTimeout을 이용해 일정 시간이 지난 후에 함수를 실행
- setInterval을 이용해 일정 시간 간격을 두고 함수를 실행하는 방법
let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
function sayHi() {
alert('안녕하세요.');
}
setTimeout(sayHi, 1000);
스케줄링 취소하기
let timerId = setTimeout(...);
clearTimeout(timerId);
- setInterval, setTimeout을 호출하고 반환받은 값을 clearInterval, clearTimeout에 넘겨주면 스케줄링을 취소할 수 있음.
call/apply와 데코레이터, 포워딩
인수로 받은 함수의 행동을 변경시켜주는 함수를 데코레이터(decorator)라고 부름.
Func.call(content,...args) : this를 명시적으로 고정해 함수를 호출할 수 있게 해주는 특별한 내장 함수 메서드
apply는 Func의 this를 content로 고정해주고, 유사배열 객체인 args를 인수로 사용할 수 있게 해줌.
call과 apply의 문법적 차이는 call이 복수 인수를 따로따로 받는 대신, apply는 인수를 유사 배열 객체로 받는다는 점
함수 바인딩
Func.bind(content)는 함수처럼 호출 가능한 특수객체를 반환. 이 객체를 호출하면 this가 context로 고정된 함수 funr가 반환.
bind는 보통 객체 메서드의 this를 고정해 어딘가에 넘기고자 할 때 사용합니다.
let boundFunc = func.bind(content);
화살표 함수 다시 살펴보기
화살표 함수에는 this가 없습니다.
arguments를 지원하지 않습니다.
new와 함께 호출할 수 없습니다.
프로퍼티 플래그와 설명자
객체엔 프로퍼티가 저장됨.
객체 프로퍼티는 값(value) 과 함께 플래그(flag)라 불리는 특별한 속성 세 가지를 갖습니다.
Writable - true면 값을 수정할 수 있음, 아니면 읽기만 가능.
enumerable - true면 반복문을 사용해 나열할 수 있음, 아니면 나열할 수 없음.
configurable - true면 프로퍼티 삭제나 플래그 수정 가능, 아니면 삭제 수정 불가능.
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj : 정보를 얻고자 하는 객체
propertyName : 정보를 얻고자 하는 객체 내 프로퍼티
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
"value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
프로퍼티 getter, setter
객체의 프로퍼티는 두 종류로 나뉩니다.
첫번째 : 데이터 프로퍼티 (data property)
두번째: 접근자 프로퍼티(accessor property)
let user = {
name: "John",
surname: "Smith"
};
let user = {
name: "John",
surname: "Smith",
get fullName() {
return `${this.name} ${this.surname}`;
}
};
alert(user.fullName); // John Smith
get
– 인수가 없는 함수로, 프로퍼티를 읽을 때 동작함set
– 인수가 하나인 함수로, 프로퍼티에 값을 쓸 때 호출됨enumerable
– 데이터 프로퍼티와 동일함configurable
– 데이터 프로퍼티와 동일함
'자바스크립트' 카테고리의 다른 글
웹 게임을 만들며 배우는 자바스크립트 예제3 (0) | 2020.09.27 |
---|---|
포로토타입과 프로토타입 상속 (0) | 2020.09.05 |
웹 게임을 만들며 배우는 자바스크립트 예제2 (0) | 2020.08.23 |
자료구조와 자료형 (0) | 2020.08.23 |
객체: 기초 (0) | 2020.08.23 |