재귀함수:
내가 나를 호출하는 것이다.
가독성이 떨어지기 때문에 실무에서 권하지는 않는다.
종료조건 체크
반복문으로 구현할 수 있는 것은 재귀함수로 모두 구현 가능하고
재귀함수로 구현 가능한 것은 반복문으로 대부분(복잡도를 증가시키면 모두) 구현 가능하다.
factorial()
*factorial(n) = n * factorial(n-1)
혹은 풀어서 쓰자면,
factorial(n) = n * (n-1) * (n-2) * ... * 1
라고 표현할 수 있다.
1부터 n까지의 모든 수를 곱한 것이다.
function factorial(n){
if(n <= 1){
return n
}
return n * factorial(n-1)
}
factorial(5)
는 아래의 과정을 통해 120이라는 값이 출력된다.
factorial(5) == 5 * factorial(4) == 5*24 == 120
factorial(4) == 4 * factorial(3) == 4*6
factorial(3) == 3 * factorial(2) == 3*2
factorial(2) == 2 * factorial(1) == 2*1
factorial(1) == 1
sigma
sigma(n) = n + sigma(n-1)
혹은 풀어서 쓰자면,
sigma(n) = n + (n-1) + (n-2) + ... + 1
라고 표현할 수 있다.
function sigma(n){
if(n <= 1){
return n
}
return n + sigma(n-1)
}
sigma(5)
는 아래의 과정을 통해 15라는 값이 출력된다.
sigma(5) == 5 + sigma(4) == 5+10 == 15
sigma(4) == 4 + sigma(3) == 4+6
sigma(3) == 3 + sigma(2) == 3+3
sigma(2) == 2 + sigma(1) == 2+1
sigma(1) == 1
function reverse(text) {
text += ''
if(text.length <= 1){
return text
}
return reverse(text.slice(1)) + text[0]
}
reverse('hello')
-> 'olleh'
text += ''
를 처음에 써줘서 텍스트가 숫자가 쓰일 상황을 대비해 안전하게 문자열로 바꿔준다.
만약 텍스트의 길이가 1보다 작거나 같으면, 텍스트 그대로 출력한다.
아니라면, 텍스트의 길이가 1이 될 때까지,
(텍스트의 첫 글자가 잘린 나머지) + (텍스트의 첫글자)
를 반복한다.
아래의 과정을 통해 'hello'라는 문자열이 'olleh'라는 문자열로 뒤집혀서 출력된다.
reverse('hello') == reverse('ello') + 'h' == 'olle' + 'h'
reverse('ello') == reverse('llo') + 'e' == 'oll' + 'e'
reverse('llo') == reverse('lo') + 'l' == 'ol' + 'l'
reverse('lo') == reverse('o') + 'l' == 'o' + 'l'
reverse('o') == 'o'
1 1 2 3 5 8 13 21 34 ...
fibo(n) = fibo(n-1) + fibo(n-2)
라고 할 수 있다.
function fibo(n){
if(n <= 2){
return n
}
return fibo(n-1) + fibo(n-2)
}
왼쪽 function
만 따라온다.
fibo(4) == fibo(3) + fibo(2) == 3 + 2
fibo(3) == fibo(2) + fibo(1) == 2 + 1
fibo(2) == 2
fibo(1) == 1
함수는 리턴해주면 휘발되기 때문에 fibo(4)
를 할 시점에서는 최종적으로 남는 값이 fibo(3)
밖에 없다. 그래서 fibo(2)
를 다시 계산해 주어야 한다.
하지만 계속 이렇게 새로 호출해주면 효율이 안 좋다.
(사진 출처: 위키피디아)
이걸 해결하기 위해 다이나믹 프로그래밍(메모이제이션, 타뷸레이션) 등을 적절히 사용할 필요가 있다.
in
은index
의 값이다.3 in [1, 2, 3] -> false 3 in [1, 1, 1, 1] -> true
in연산자 - mozilla 참고링크:
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/in
function scope
, 메모리 효율익명 즉시 실행 함수
(function () {
let a = 1;
let b = 2;
return a + b;
}());
기명 즉시 실행 함수
(function foo() {
let a = 3;
let b = 5;
return a * b;
}());
foo();
-> ReferenceError: foo is not defined
기명 즉시 실행 함수는 어차피 실행하지 못해서 의미가 없다.
메모리를 효율적으로 관리하기 위해 바로 실행해야 하는 것들을 즉시 실행함수로 관리한다.
map
* let data = [{
반 : 1,
번 : 1,
이름 : "호준",
중간고사점수 : 55
}, {
반 : 1,
번 : 2,
이름 : "길동",
중간고사점수 : 60
}, {
반 : 1,
번 : 3,
이름 : "영희",
중간고사점수 : 30
}, {
반 : 1,
번 : 4,
이름 : "철수",
중간고사점수 : 20
}, {
반 : 1,
번 : 5,
이름 : "규리",
중간고사점수 : 100
}]
data.map(x => x.중간고사점수)
-> (5) [55, 60, 30, 20, 100]
map
을 사용하면, 정보를 뽑아내는게 간단하다.
data.map(x => [x.이름, x.중간고사점수])
-> [Array(2), Array(2), Array(2), Array(2), Array(2)]
// 0: (2) ['호준', 55]
// 1: (2) ['길동', 60]
// 2: (2) ['영희', 30]
// 3: (2) ['철수', 20]
// 4: (2) ['규리', 100]
중간고사 합계↓
let s = 0;
data.map(x => x.중간고사점수).forEach(y => s+= y)
//x => x.중간고사점수
//y => s+=y이게 콜백함수
s
-> 265
forEach (콜백함수)
forEach- mozilla 참고링크: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
콜백함수: forEach처럼 함수의 매개변수로 함수를 받게 되면 그때 괄호 안에 들어가는 함수가 콜백함수
let data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let newData = data.map(a => a.map(n => n *2));
배열이 두개 있어서 맵을 두번 사용했다.
여기서 a는 [1,2,3] 안에 있는 숫자를 두배로 해서(n => n*2
) 넣어준다.
Call by Value
자바스크립트는 모두 call by value
이다. call by reference
는 없다.
call by value
는 참조값에 대한 복사본을 만들어서 넘긴다.
다만, array
나 object
는 index
로 메모리 주소를 따라가 값을 처리하는데 여기에서는
주소값이 재할당된 것이 아니고 속성이 변경된 것이어서 call by reference
처럼 보이는 것뿐이고, 실제로는 그냥 call by value
이다.
여러가지 값이 들어가 있는 것은 변경 된다.(array, object, function
)
let x = [10, 20, 30]
function test(a) {
a[0] = 10000;
}
test(x)
x
-> (3) [10000, 20, 30]
let x = 10;
function test(a) {
a[0] = 10000;
}
test(x)
x
-> 10
array
나 object
를 넘길 때는 주의를 해야한다!
filter
filter - mozilla 참고링크: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
mdn 예제
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
-> (3) ['exuberant', 'destruction', 'present']
//true인 것만 가져온다.
let x = [2, 6, 5, 4, 8, 5, 4, 3, 2, 9]
x.filter(z => z > 5);
-> (3) [6, 8, 9]
// 5를 초과하는 숫자만
로그인정보
let 회원정보 = [{
아이디 : 'jjang',
패스워드 : '5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5',
성별 : '남',
휴대폰번호 : '010-5004-0000',
이메일 : 'hojun1@gmail.com',
가입연도 : '2020-12-02',
주접속위치 : '125.242.161.149'
}, {
아이디 : 'jjang2',
패스워드 : '5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5',
성별 : '남',
휴대폰번호 : '010-5004-0000',
이메일 : 'hojun2@gmail.com',
가입연도 : '2021-12-02',
주접속위치 : '125.242.161.149'
}, {
아이디 : 'jjang3',
패스워드 : '5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5',
성별 : '여',
휴대폰번호 : '010-5004-0000',
이메일 : 'hojun3@gmail.com',
가입연도 : '2021-12-02',
주접속위치 : '125.242.161.149'
}, {
아이디 : 'jjang4',
패스워드 : '5dbfbded05f34de754e8f265448be17934580556b488c0461fd12d211a87aaa5',
성별 : '여',
휴대폰번호 : '010-5004-0000',
이메일 : 'hojun4@gmail.com',
가입연도 : '2020-12-02',
주접속위치 : '125.242.161.149'
}];
1번. 남자인 사람
회원정보.filter(el => el.성별 === '남')
2번. 남자이면서 2021년도에 가입하신 분
내답
회원정보.filter(el => (el.성별 === '남')*(el.가입연도.slice(0,4) === '2021'))
나도 모르게 엑셀에서 조건을 줄 때 썼던 '*'를 써버렸다.. 논리연산자'&&'를 사용했어야 했는데!
정답
회원정보.filter(el => el.성별 === "남" && el.가입연도.split("-")[0] === "2021")
3번. 아이디가 'jjang'인 사람(find 사용)
filter
사용했을 때: 회원정보.filter(el => el.아이디 === "jjang")
find
사용했을 때:회원정보.find(element => element.아이디 === "jjang");
find - mozilla 참고링크:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
filter
는 다 둘러보면서 맞는지 확인한다.
find
는 찾으면 멈춘다. 아이디는 겹치지 않으니(겹쳐도 태그가 있음), 효율이 좋다.
게시판 검색
let blogs = [{
id : 1,
title : 'title1',
content : 'content1',
section : '일상',
}, {
id : 2,
title : 'title2',
content : 'content2',
section : '취미',
}, {
id : 3,
title : 'title3',
content : 'content3',
section : '개발',
}, {
id : 4,
title : 'title4',
content : 'content4',
section : '개발',
}];
let s = '개발'
let data = s? blogs.filter(b => b.section === s): blogs;
object가 순회가 안 된다. 이터러블하지 않다. 그래서 Map이 생겼다..
Map은 순회가 된다.
let m = new Map();
Map에 값을 넣기
m.set('하나', '1');
m.set(1, '하나');
m.set(true, 1);
m.set(false, 0);
Map의 값에 접근하기
console.log(m.get('하나'));
console.log(m.get(true));
Map의 값이 있는지 확인하기
console.log(m.has('하나'));
Map의 값을 제거하기
console.log(m.delete('하나'));
console.log(m.has('하나'));
console.log(m);
Map의 크기를 확인하기
console.log(m.size);
array
의 데이터 타입이 object
로 찍히는 것처럼
Map
도 object
로 찍힌다.
인덱스를 가지는 자료형을 맵핑하기
교집합, 차집합, 합집합을 구하는게 가능하다.
Set
은 순서가 없다. index
로 호출 못한다.
let s = new Set('abcdeeeeeeeee');
console.log(s);
console.log(s.size);
-> Set(5) {'a', 'b', 'c', 'd', 'e'}
-> 5
s.add('f')
-> Set(6) {'a', 'b', 'c', 'd', 'e', …}
s.delete('b')
-> true
s
-> Set(5) {'a', 'c', 'd', 'e', 'f'}
s.has('c')
-> true
s.size
-> 5
s.forEach(i=> console.log(i))
-> a
-> c
-> d
-> e
-> f
s.add('f');
-> Set(5) {'a', 'c', 'd', 'e', 'f'}
//중복이 안 된다.
let a = new Set('abc');
let b = new Set('cde');
a
-> Set(3) {'a', 'b', 'c'}
b
-> Set(3) {'c', 'd', 'e'}
교집합
[...a].filter(value => b.has(value))
-> ['c']
합집합
let union = new
Set([...a].concat(...b))
union
-> Set(5) {'a', 'b', 'c', 'd', 'e'}
new Set([...a, ...b]); 이것도 똑같이 나온다..
[...a] 전개표현식
function add(...x){ return x; }
add(1, 2, 3, 4, 5)
모던자바스크립트 예제(https://ko.javascript.info/rest-parameters-spread)let arr1 = [1, -2, 3, 4]; let arr2 = [8, 3, -8, 1]; Math.max(1, ...arr1, 2, ...arr2, 25); // 25
모던자바스크립트 예제(https://ko.javascript.info/rest-parameters-spread)
let arr = [3, 5, 1]; let arr2 = [8, 9, 15]; let merged = [0, ...arr, 2, ...arr2];
아예 이렇게 어려워버리니까 더 집중하는 것 같다. 너무 어려우니까, 아주 조금이라도 이해하려면 집중을 하지 않으면 안 된다. 짧은 기간동안 배우는만큼 최대한 많이 배워가야지..! 그치만 어렵다 증말.
잘한점
고칠점