
제로베이스 자바스크립트 기초개념 심화학습 부분 정리
축약된 부분이 존재할 수 있습니다.
심화적인 부분 학습
함수 재귀(Recursion)란, 함수가 자기 자신을 호출하는 것을 말합니다.
let i = 0;
function a() {
console.log(i, "A");
i += 1;
if (i < 4) {
a();
}
}
a();
이런식으로 함수안에서 자신을 부르는 것을 함수 재귀라고 한다.
그럼 유용하게 어떻게 사용될 수 있을까?
const neo = { name: "Neo" };
const evan = { name: "Evan", parent: neo };
const lewis = { name: "Lewis", parent: evan };
const amy = { name: "Amy", parent: lewis };
const getRootUser = (user) => {
if (user.parent) {
return getRootUser(user.parent);
}
return user;
};
console.log(getRootUser(amy));
사용하는 예시를 보면 이런식으로 유용하게 사용할 수도 있다.
일반 함수와 화살표 함수에 따라 다르게 정의됩니다. <- 가장 중요한 부분
일반 함수는 호출 위치에서 this가 정의됩니다.
화살표 함수는 선언 위치(렉시컬 스코프)에서 this가 정의됩니다.
function User() {
this.name = "User";
return {
name: "Neo",
age: 80,
// getInfo() {
// return `${this.name}는 ${this.age}입니다.`;
// },
getInfo: () => {
return `${this.name}는 ${this.age}입니다.`;
},
};
}
const u = new User();
console.log(u.name);
console.log(u.age);
console.log(u.getInfo());
const evan = {
name: "Evan",
age: 25,
};
console.log(u.getInfo.call(evan));
일반 함수는 자신이 호출되는 범위인 return 안에 객체의 name과 age를 출력시키고
화살표 함수는 호출된 함수를 감싸는 가장 가까운 함수가 this를 정의하는 영역이 되어 name은 User가 뜨고 age는 undefined가 뜬다.
그래서 this키워드를 사용할 때, 일반 함수를 사용할 것인지 화살표 함수를 사용할 것인지 정하고 코드를 작성해야 한다.
const neo = {
name: "Neo",
};
const amy = {
name: "Amy",
getInfo(age, city) {
return `${this.name}는 ${age}세이고, ${city}에 삽니다.`;
},
};
console.log(amy.getInfo(22, "서울"));
// .call(this, 인수1, 인수2, ...)
// 대상 함수를 주어진 객체(this)의 메소드로 실행합니다.
console.log(amy.getInfo.call(neo, 1, "부산"));
// .apply(this, [인수1, 인수2, ...])
// 대상 함수를 주어진 객체(this)의 메소드로 실행합니다.
console.log(amy.getInfo.apply(neo, [85, "서울"]));
// .bind(this)
// 대상 함수를 주어진 객체(this)의 메소드로 실행할 수 있는 새로운 함수를 반환합니다.
const neoGetInfo = amy.getInfo.bind(neo);
setTimeout(() => {
console.log(neoGetInfo(85, "서울"));
}, 1000);
3가지 모두 함수가 없는 객체에게 함수를 빌려주는 느낌은 비슷하다.
Throttle & Debounce같은 경우는 직접 구현하기가 쉽지 않기에 외부 라이브러리를 사용해 볼 것이다.
https://lodash.com/
CDN copies에 들어가서
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
이러한 코드를 복사해주고 html에 붙여넣기 해주자. (main.js위에)
// Throttle
// - 정해진 시간 간격으로 함수를 실행하도록 제한합니다.
window.addEventListener(
"scroll",
_.throttle(function () {
console.log("Scroll!");
}, 400),
);
// Debounce
// - 정해진 시간 동안 함수가 실행되지 않으면, 함수를 실행합니다.(마지막에 한 번만 실행)
async function getMovies(movieName) {
const res = await fetch(
`https://omdbapi.com/?apikey=7035c60c&s=${movieName}`,
);
return await res.json;
}
const inputEl = document.querySelector("input");
inputEl.addEventListener(
"input",
_.debounce(function () {
console.log(getMovies(inputEl.value));
}, 400),
)
둘 다 함수의 실행 횟수를 제한하는 느낌이다. (최적화!)
불변성
원시형(문자, 숫자, 불린, null, undefined)은 불변성
참조형(배열, 객체, 함수)는 가변성을 가지고 있습니다.
const a = 1;
let b = a;
b = 2;
console.log(a);
console.log(b);
const c = { x: 1, y: 2 };
const d = c;
d.x = 99;
console.log(c);
console.log(d);
const e = [1, 2, 3];
const f = e;
f[0] = 99;
console.log(e);
console.log(f);

이런식으로 작동이 되는 것을 알고 있으면 나중에 에러를 줄일 수 있다.
(코드를 순서대로 보며 사진처럼 생각해보자)
이것으로 알 수 있는 것은 참조형같은 경우는 우리가 원치않게 변하게 만드는 일이 생길 수 있기때문에 복사를 해서 사용을 해줘야 한다.
근데 여기서 복사는 두 가지로 나뉘게 된다.
그래서 다음시간에는 얕은 복사와 깊은 복사에 대해서 공부해 볼 것이다.
얕은 복사(Shallow copy)
const a = [
{ x: 1, y: [1, 2] },
{ x: 2, y: [3, 4] },
{ x: 3, y: [5, 6] },
];
여기 여러 레벨을 가지고 있는 배열이 있다.
여기서 얕은 복사는 최상위인 배열만을 복사하는 것이고, 깊은 복사는 저 배열의 모든 레벨을 복사하는 것이다. (배열, 배열안 객체, 배열안 객체안 배열도 복사)
const a = [
{ x: 1, y: [1, 2] },
{ x: 2, y: [3, 4] },
{ x: 3, y: [5, 6] },
];
const c = { x: 1, y: 2 };
// const d = c;
// 얕은 복사 2개
const d = Object.assign({}, c);
// const d = { ...c };
d.x = 99;
console.log(c);
console.log(d);
const e = [1, 2, 3];
// const f = e;
// 얕은 복사 2개
// const f = e.slice() // 똑같은 얕은 복사가 되지만 의미가 자른다는 의미여서 않좋음
const f = [...e];
console.log(e);
console.log(f);
const g = [
{ x: 1, y: 2 },
{ x: 3, y: 4 },
];
// 깊은 복사
const h = _.cloneDeep(g);
h[0].x = 99;
console.log(g);
console.log(h);
얕은 복사는 간단하게 할 수 있지만, 깊은 복사같은 경우는 구현하기가 어렵기에 lodash의 함수를 사용해서 깊은 복사를 해주었다.
깊은 복사는 레벨이 얼마나 존재하는지 모르기에 성능상 불안정적인 부분이 있어서 얕은 복사로 최대한 해결해보고 안된다고 하면 깊은 복사를 사용하는게 맞다.
즉, 그냥 상황에 맞게 얕은 복사와 깊은 복사를 사용하면 된다.