본문 바로가기

자바스크립트

함수 심화학습, 객체 프로퍼티 설정

함수_심화학습_객체_프로퍼티_설정

재귀와 스택

재귀의 베이스(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)' 함수.

렉시컬 환경

  1. 변수

자바스크립트에선 실행 중인 함수, 코드블록 {...}, 스크립트 전체는 렉시컬 환경(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"
  1. 함수 선언문

함수 선언문으로 선언한 함수는 일반 변수와는 달리 바로 초기화 된다는 점에서 차이가 있음.

let say = function(name)... 함수 표현식은 해당되지 않음.

//execution start 		// => phrase: <uninitialized>
											// 	say : function

let phrase = "Hello";
function say(name) {
  
}
  1. 내부와 외부 렉시컬 환경

함수를 호출해 실행하면 새로운 렉시컬 환경이 자동으로 만들어집니다. 이 렉시컬 환경엔 함수 호출 시 넘겨받은 매개변수와 함수의 지역 변수가 저장됩니다.

let phrase = "Hello";
function say(name) {
  alert(`${phrase}, ${name}`);
}
say("kim"); //"Hello, kim"
  • 내부 렉시컬 환경은 현재 실행 중인 함수인 say에 상응. 내부 렉시컬 환경엔 함수의 인자인 name으로부터 유래한 프로퍼티 하나만 있음. name = kim
  • 외부 렉시컬 환경은 전역 렉시컬 환경, 전역 렉시컬 환경은 phrase와 함수 say를 프로퍼티로 갖음.
  • 내부 렉시컬 환경은 외부 렉시컬 환경에 대한 참조를 갖음.
  • 코드에서 변수에 접근할 땐 먼저 내부 렉시컬 환경을 검색 범위로 잡음 -> 못찾은 경우 내부 렉시컬 환경이 참조하는 외부 렉시컬 환경으로 확장함.
  1. 반환함수
  • 모든 함수는 함수가 생성된 곳의 렉시컬 환경을 기억한다는 점.
  • 함수는 [[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 – 데이터 프로퍼티와 동일함