[230412] 함수 | 재귀함수 | 스코프 | 호이스팅 | 클로저 | Map

윤지수·2023년 4월 12일
0
post-thumbnail

🪄 함수

object처럼 사용된다

function 함수(a, b) {
  return a + b;
}

console.dir(함수);
// length: 파라미터의 개수

함수["location"] = "jeju";
console.dir(함수); // 변경 O

함수["name"] = "jejufunction";
console.dir(함수); // 변경 X
// console.dir(jejufunction) // 호출 불가능

함수["length"] = 5;
console.dir(함수); // 변경 X

아규먼트가 순서대로 들어간다

function 함수(a = 10, b = 20, c = 30) {
  return a + b + c;
}

함수(100);								// 150
함수(100, 200);							// 330
함수((c = 300)); 						// 330이 아니고 350
함수((a = 100), (c = 300));				// 430
함수((a = 100), (b = 300), (c = 200));	// 순서가 뒤바뀌면 hell
  • roro 기법
    입력되는 아규먼트가 매우 많은 함수의 경우 호출하는 쪽에서 어떤 값이 들어가는지 명확히 알기 어렵고 읽기도 어렵다.
    원리: 구조분해할당
function 로그인정보({ 
	회원등급 = "Gold",
	글쓰기 = true,
	글읽기 = true,
	채널관리 = true,
	백업 = "일주일 이내 가능",
	소셜로그인여부 = true }) {
  console.log(회원등급, 글쓰기, 글읽기, 채널관리, 백업, 소셜로그인여부);
}

로그인정보({
  회원등급: "Silver",
  소셜로그인여부: false, // 순서까지 바뀌었다
  백업: "3일 이내 가능", // 중간에 생략된 값도 있고
});
// Silver true true true 3일 이내 가능 false

로그인정보(); // error
로그인정보({}); // Gold true true true 일주일 이내 가능 true
// 아규먼트 없이 호출 가능
function 함수({ a = 1, b = 2, c = 3 } = {}) {
  console.log(a, b, c);
  return a + b + c;
}

함수(); 				// 1 2 3	// 6
함수({}); 			// 1 2 3	// 6
함수({ b: 100 });	// 1 100 3	// 104

재귀함수

함수가 자기 자신을 호출하는 것

반복문으로 반복할 수 있는 것은 재귀함수로 만들 수 있다
종료 조건이 없으면 무한반복이기 때문에 보통 재귀 호출이 멈출 수 있는 종료 조건을 체크해야 한다

// 팩토리얼
function factorial(n) {
  if (n <= 1) {	// 종료 조건
    return n; // early return
  }
  return n * factorial(n - 1);
}
// 문자열 뒤집기
function reverse(txt) {
  if (txt.length <= 1) {
    return txt;
  }
  return reverse(txt.slice(1)) + txt[0];
}

즉시 실행 함수는 외부에서 호출할 수 없다
메모리를 효율적으로 관리하기 위해 즉시 실행 함수를 사용한다
바로 실행해야 하는 것들을 즉시 실행 함수로 관리한다

// 익명 즉시 실행 함수
(function () {
  let a = 1;
  let b = 2;
  return a + b;
})();

// 기명 즉시 실행 함수
(function foo() {
  let a = 3;
  let b = 5;
  return a * b;
})();

foo(); // error

call by value

JavaScript는 call by value만 존재한다
객체자료형의 경우가 call by reference인 것처럼 보이지만, 사실 주소가 넘어가는 것이 아니라 주소값이 복사가 되어 넘어간다

let test1 = [10, 20, 30];
function 값변경1(arr) {
	arr[0] = 100;
}
값변경1(test1);
console.log(test1);	// [100, 20, 30]

let test2 = 100;
function 값변경2(value) {
  value = 1000;
}
값변경2(test2);
console.log(test2);	// 100
// call by reference 반례
var a = {};
function test(b) {
  b = 1000;
}
test(a);
console.log(a); // {}, 다른 언어의 경우 call by ref로 동작하여 a의 값이 1000으로 바뀐다

🪄 스코프

변수에 접근할 수 있는 유효범위

  • 전역 스코프
  • 함수 스코프
  • 블록 스코프(ES6)

스코프 체이닝
스코프 체인을 따라 해당 영역에 변수가 없으면 계속해서 상위 스코프를 따라 올라간다. 전역까지 올라갔는데도 변수가 없다면 에러가 난다.

🪄 호이스팅

변수나 함수 선언문이 해당 스코프의 최상단으로 끌어올려지는 현상
인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다

console.log(함수(10)); // 110

function 함수(x) {
  return x + 100;
}

let, const, class 이용한 선언문은 호이스팅이 되었지만 안된 것'처럼' 동작한다
일시적 사각지대(Temporal Dead Zone)에 빠지기 때문이다
오류가 나는 이유는 var 키워드는 선언과 함께 undefined로 초기화되지만 let과 const는 초기화 되지 않는 상태로 선언만 메모리 저장되기 때문이다

let foo = 1;
{
  console.log(foo);	// 일시적 사각지대	
  					// Cannot access 'foo' before initialization
  let foo = 2;
}

🪄 클로저

폐쇄된 공간 안의 데이터에 접근하기 위한 테크닉
변수 은닉과 메모리 효율, 코드 효율(또는 완전성)을 극대화하기 위해 사용한다

function 제곱(x) {
  function 승수(y) {
    return y ** x;
  }
  return 승수;
}

let 제곱2 = 제곱(2);	// 2 제곱해주는 함수
let 제곱3 = 제곱(3);	// 3 제곱해주는 함수
let 제곱4 = 제곱(4);	// 4 제곱해주는 함수

console.log(제곱2(2));	// 4
console.log(제곱2(3));	// 9
console.log(제곱2(4));	// 16
function 승수제조기() {
  let value = 0;
  function 승수() {
    return (++value) ** 2;
  }
  return 승수;
}

let= 승수제조기();

console.log(());	// 1
console.log(());	// 4
console.log(());	// 9

console.log(value);	// 출력할 수 없다. 은닉화

🪄 생성자 함수

자바스크립트에서 객체를 생성하는 방식

  • 객체 리터럴 표현식
  • 생성자 함수 이용

일반 함수와 구분하기 위해 생성자 함수 이름의 첫 글자는 대문자로 시작한다
new 연산자를 붙여 실행해야 한다. new 연산자는 생성자 함수의 this 가 인스턴스를 바라보도록 만들어주는 역할을 한다.

let book = {
  책이름: "JavaScript",
  책가격: 10000,
  저자: "윤말랑",
  출판일: "2023.04.12",
};

function Book(책이름, 책가격, 저자, 출판일) {
  this.책이름 = 책이름;
  this.책가격 = 책가격;
  this.저자 = 저자;
  this.출판일 = 출판일;
}

let newBook = Book("JavaScript", 10000, "윤말랑", "2023.04.12");
console.log(newBook); // undefined(반환값) // return 값이 없기 때문

let newBook2 = new Book("JavaScript", 10000, "윤말랑", "2023.04.12");
console.log(newBook2);	// Book {책이름: 'JavaScript', 책가격: 10000, 저자: '윤말랑', 출판일: '2023.04.12'}

/* 
new 키워드를 사용했을 때의 동작
function Book(책이름, 책가격, 저자, 출판일) {
  this = {};
  this.책이름 = 책이름;
  this.책가격 = 책가격;
  this.저자 = 저자;
  this.출판일 = 출판일;
  return this;
}
*/

🪄 Map

키-값 쌍을 가지는 객체 자료형

Map과 Object의 차이

  • Map은 데이터를 추가하거나 제거하는 작업에서 Object보다 더 나은 성능을 보인다
let m = new Map();

// Map에 값 넣기
m.set("하나", 1);
m.set(1, "하나");
m.set(true, 1).set(false, 0);

console.log(m);	// Map(4) {'하나' => 1, 1 => '하나', true => 1, false => 0}

// Map의 값에 접근하기
console.log(m.get("하나"));	// 1

// Map의 값 확인하기
console.log(m.has("하나"));	// true

// Map의 값 제거하기
m.delete("하나");
console.log(m.has("하나"));	// false
  • Object의 키는 문자열 타입으로만 지정해야 하지만, Map의 키는 모든 값을 가질 수 있다
// {[1, 2, 3]: 100, {'하나':1}: 10} // error
m.set([1, 2, 3], '리얼리?');
console.log(m.get([1, 2, 3]));	// undefined
								// 두 배열의 주소값이 다르기 때문
const x = [1, 2, 3, 4];
m.set(x, "리얼리?");
console.log(m.get(x));	// 리얼리?
  • Object는 크기를 사용자가 직접 수동으로 알아내야 하지만, Map은 size를 통해 크기를 쉽게 얻을 수 있다
// Map의 크기 확인하기
const obj = {하나: 1,: 2};
console.log(obj.length);	// undefined
console.log(obj.size);		// undefined

console.log(m.size);		// 5
  • Object는 직접 순회가 불가능하지만, Map은 직접 순회가 가능하다
let data = { one: 1, two: 2 };

for (const i of data) {
  console.log(i);	// data is not iterable
}

for (const i of Object.entries(data)) {
  console.log(i);
}
// ['one', 1]
// ['two', 2]

// 직접 순회가 가능한 Map
let m = new Map();

m.set("하나", 1) // 메서드 체이닝
  .set("둘", 2)
  .set("셋", 3)
  .set("넷", 4);

for (const [key, value] of m) {
  console.log(key, value);
}
// 하나 1
// 둘 2
// 셋 3
// 넷 4
  • Map은 메서드로 모두 호출 가능하다
let test = { one: 1, two: 2 };
Object.keys(test);	// ['one', 'two']

console.log(m.keys());
console.log(m.values());
console.log(m.entries());

// Map <-> Object 간의 형변환
let= new Map(Object.entries({ one: 1, two: 2 }));
let 오브젝트 = Object.fromEntries();

0개의 댓글